freeRTOS源码解析1--堆栈管理1
freeRTOS源码解析1--堆栈管理1
freeRTOS有5种方式管理内存,源码路径:FreeRTOS\portable\MemMang,每种方式为1个.c文件--heap_1.c~heap_5.c。
本人暂时只了解了heap_4.c的管理方式,若有理解不到位的地方,请指正。
从申请内存函数:pvPortMalloc开始逐步解析,首先是源代码:
1 1 void * pvPortMalloc( size_t xWantedSize ) 2 2 { 3 3 BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink; 4 4 void * pvReturn = NULL; 5 5 6 6 vTaskSuspendAll(); 7 7 { 8 8 /* If this is the first call to malloc then the heap will require 9 9 * initialisation to setup the list of free blocks. */ 10 10 /* 如果是第一次调用,那么堆会请求初始化来创建空闲内存块的链表 */ 11 11 if( pxEnd == NULL ) 12 12 { 13 13 prvHeapInit(); 14 14 } 15 15 else 16 16 { 17 17 mtCOVERAGE_TEST_MARKER(); 18 18 } 19 19 20 20 /* Check the requested block size is not so large that the top bit is 21 21 * set. The top bit of the block size member of the BlockLink_t structure 22 22 * is used to determine who owns the block - the application or the 23 23 * kernel, so it must be free. */ 24 24 /* 检查申请的块大小是否大到最高比特位置位了。结构体BlockLink_t中 25 25 指示块大小的成员的最高比特位用于确定是谁拥有该内存块 - 应用程序还是内核, 26 26 因此该位必须是空闲的 */ 27 27 if( ( xWantedSize & xBlockAllocatedBit ) == 0 ) 28 28 { 29 29 /* The wanted size must be increased so it can contain a BlockLink_t 30 30 * structure in addition to the requested amount of bytes. */ 31 31 /* 请求的块大小必须增加,这样除了请求的字节数外,还能包含BlockLink_t结构体 */ 32 32 if( ( xWantedSize > 0 ) && 33 33 ( ( xWantedSize + xHeapStructSize ) > xWantedSize ) ) /* Overflow check */ 34 34 { 35 35 xWantedSize += xHeapStructSize; 36 36 37 37 /* Ensure that blocks are always aligned. */ 38 38 /* 确保块始终是对齐的(8字节对齐) */ 39 39 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) 40 40 { 41 41 /* Byte alignment required. Check for overflow. */ 42 42 /* 需要字节对齐。检查溢出。 */ 43 43 if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) ) 44 44 > xWantedSize ) 45 45 { 46 46 xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); 47 47 configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 ); 48 48 } 49 49 else 50 50 { 51 51 xWantedSize = 0; 52 52 } 53 53 } 54 54 else 55 55 { 56 56 mtCOVERAGE_TEST_MARKER(); 57 57 } 58 58 } 59 59 else 60 60 { 61 61 xWantedSize = 0; 62 62 } 63 63 64 64 if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) 65 65 { 66 66 /* Traverse the list from the start (lowest address) block until 67 67 * one of adequate size is found. */ 68 68 /* 从头部块(最低地址)开始遍历列表,直到找到一个足够大的块 */ 69 69 pxPreviousBlock = &xStart; 70 70 pxBlock = xStart.pxNextFreeBlock; 71 71 72 72 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) 73 73 { 74 74 pxPreviousBlock = pxBlock; 75 75 pxBlock = pxBlock->pxNextFreeBlock; 76 76 } 77 77 78 78 /* If the end marker was reached then a block of adequate size 79 79 * was not found. */ 80 80 /* 如果遍历到了接尾块,那么无法找到一个足够大的块 */ 81 81 if( pxBlock != pxEnd ) 82 82 { 83 83 /* Return the memory space pointed to - jumping over the 84 84 * BlockLink_t structure at its start. */ 85 85 /* 返回指向的内存空间——跳过头部的BlockLink_t结构体。 */ 86 86 pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize ); 87 87 88 88 /* This block is being returned for use so must be taken out 89 89 * of the list of free blocks. */ 90 90 /* 此块要被返回使用,所以必须移出空闲块的链表 */ 91 91 pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; 92 92 93 93 /* If the block is larger than required it can be split into 94 94 * two. */ 95 95 /* 如果此块大于申请的内存,那么可以拆分成两个块 */ 96 96 if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) 97 97 { 98 98 /* This block is to be split into two. Create a new 99 99 * block following the number of bytes requested. The void 100 100 * cast is used to prevent byte alignment warnings from the 101 101 * compiler. */ 102 102 /* 此块要被分成两个块。创建一个紧随申请空间后的新块。强制转换成 103 103 void以防止编译器上报字节对齐警告。 */ 104 104 pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); 105 105 configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 ); 106 106 107 107 /* Calculate the sizes of two blocks split from the 108 108 * single block. */ 109 109 /* 计算从单个块中分离出的两个块的大小。 */ 110 110 pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; 111 111 pxBlock->xBlockSize = xWantedSize; 112 112 113 113 /* Insert the new block into the list of free blocks. */ 114 114 /* 将新块插入到空闲块列表中。 */ 115 115 prvInsertBlockIntoFreeList( pxNewBlockLink ); 116 116 } 117 117 else 118 118 { 119 119 mtCOVERAGE_TEST_MARKER(); 120 120 } 121 121 122 122 xFreeBytesRemaining -= pxBlock->xBlockSize; 123 123 124 124 if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) 125 125 { 126 126 xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; 127 127 } 128 128 else 129 129 { 130 130 mtCOVERAGE_TEST_MARKER(); 131 131 } 132 132 133 133 /* The block is being returned - it is allocated and owned 134 134 * by the application and has no "next" block. */ 135 135 /* 此块将被返回--被分配并为应用程序所持有,并且此块没有指向“下一块”。 */ 136 136 pxBlock->xBlockSize |= xBlockAllocatedBit; 137 137 pxBlock->pxNextFreeBlock = NULL; 138 138 xNumberOfSuccessfulAllocations++; 139 139 } 140 140 else 141 141 { 142 142 mtCOVERAGE_TEST_MARKER(); 143 143 } 144 144 } 145 145 else 146 146 { 147 147 mtCOVERAGE_TEST_MARKER(); 148 148 } 149 149 } 150 150 else 151 151 { 152 152 mtCOVERAGE_TEST_MARKER(); 153 153 } 154 154 155 155 traceMALLOC( pvReturn, xWantedSize ); 156 156 } 157 157 ( void ) xTaskResumeAll(); 158 158 159 159 #if ( configUSE_MALLOC_FAILED_HOOK == 1 ) 160 160 { 161 161 if( pvReturn == NULL ) 162 162 { 163 163 extern void vApplicationMallocFailedHook( void ); 164 164 vApplicationMallocFailedHook(); 165 165 } 166 166 else 167 167 { 168 168 mtCOVERAGE_TEST_MARKER(); 169 169 } 170 170 } 171 171 #endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */ 172 172 173 173 configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 ); 174 174 return pvReturn; 175 175 }
以下开始逐行分析代码:
vTaskSuspendAll(); // 暂停调度器
if( pxEnd == NULL ) // 第一次运行的话,pxEnd为NULL,走下面分支
{
prvHeapInit();
}
prvHeapInit():
1 static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */ 2 { 3 BlockLink_t * pxFirstFreeBlock; 4 uint8_t * pucAlignedHeap; 5 size_t uxAddress; 6 size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; 7 8 /* Ensure the heap starts on a correctly aligned boundary. */ 9 /* 确保堆开始于对齐的边界上 */ 10 // static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; 11 // #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 75 * 1024 ) ) 12 // ucHeap是一个很大的数组 13 // #define portBYTE_ALIGNMENT 8 14 // #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) 15 16 uxAddress = ( size_t ) ucHeap; 17 // uxAddress & 7非0,那么ucHeap起始地址未8字节对齐 18 // 用keil仿真时,我的uxAddress = 0x200001EC 19 if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) 20 { 21 uxAddress += ( portBYTE_ALIGNMENT - 1 ); // uxAddress = uxAddress + 7 = 0x200001F3 22 uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); // uxAddress = uxAddress & 0xFFFFFFF8 = 0x200001F0 23 xTotalHeapSize -= uxAddress - ( size_t ) ucHeap; // xTotalHeapSize = 75k - (uxAddress - ucHeap) = 75k - 4 24 } 25 // 总空间少了4个字节,相当于堆的起始地址向后移动4个字节 26 // pucAlignedHeap = 0x200001F0 = 0x200001EC + 4 27 pucAlignedHeap = ( uint8_t * ) uxAddress; 28 29 /* xStart is used to hold a pointer to the first item in the list of free 30 * blocks. The void cast is used to prevent compiler warnings. */ 31 /* xStart用于保存指向空闲链表的第一个链表项的指针。 32 * 强转成void是用于防止编译器报告警。 */ 33 xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; 34 xStart.xBlockSize = ( size_t ) 0; 35 36 /* pxEnd is used to mark the end of the list of free blocks and is inserted 37 * at the end of the heap space. */ 38 /* pxEnd用于标记空闲块链表的最后一项,并且被插入到堆空间的最后。 */ 39 // const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( 8 - 1 ) ) ) & ~( ( size_t ) 7 ); 40 // xHeapStructSize = (8 + 7) & 0xFFFFFFF8 = 8 41 // 堆的尾部也要进行对齐 42 uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize; // uxAddress = 0x200001F0 + 75k - 4 = 0x20012DEC 43 uxAddress -= xHeapStructSize; // uxAddress = 0x20012DEC - 8 = 0x20012DE4 44 uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); // uxAddress = 0x20012DE4 & 0xFFFFFFF8 = 0x20012DE0 45 pxEnd = ( void * ) uxAddress; 46 pxEnd->xBlockSize = 0; 47 pxEnd->pxNextFreeBlock = NULL; 48 // 堆的尾地址向前移动12个字节,其中前8个字节存放链表的最后一项,并用pxEnd指向该项 49 50 /* To start with there is a single free block that is sized to take up the 51 * entire heap space, minus the space taken by pxEnd. */ 52 /* 有一个空闲块,它的大小相当于整个堆空间,减去pxEnd占用的空间。 */ 53 pxFirstFreeBlock = ( void * ) pucAlignedHeap; // 堆头部存放链表的第一项 54 pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock; // 可用的空闲堆空间(包含了该项的8个字节):75k-4-12=0x12BF0 55 pxFirstFreeBlock->pxNextFreeBlock = pxEnd; // 下一块指向尾部 56 57 /* Only one block exists - and it covers the entire usable heap space. */ 58 /* 只存在一个块,并且它包含整个可用的堆空间 */ 59 xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; // xMinimumEverFreeBytesRemaining = 0x12BF0 60 xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; // xFreeBytesRemaining = 0x12BF0 61 62 /* Work out the position of the top bit in a size_t variable. */ 63 /* 计算出size_t变量最高比特位的位置 */ 64 // xBlockAllocatedBit = 1 << (8 *4 - 1) = 0x80000000 65 xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); 66 }
执行完后,heap的示意图:
回到pvPortMalloc
1 // 在prvHeapInit后,xBlockAllocatedBit就不再改变,将一直等于0x80000000 2 // 假设xWantedSize = 105 3 if( ( xWantedSize & xBlockAllocatedBit ) == 0 ) 4 { 5 if( ( xWantedSize > 0 ) && ( ( xWantedSize + xHeapStructSize ) > xWantedSize ) ) /* Overflow check */ 6 { 7 xWantedSize += xHeapStructSize; // xWantedSize = xWantedSize + 8 = 0x71 8 9 // 未对齐 10 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) 11 { 12 // (0x71 + (8 - (0x71 & 0x7))) = 0x78 > 0x71 13 if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) ) > xWantedSize ) 14 { 15 // xWantedSize = xWantedSize + (8 - (0x71 & 0x7)) = 0x78 16 // 把想要的大小扩展7个字节,使其大小8字节对齐 17 xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); 18 } 19 else 20 { 21 xWantedSize = 0; // 加7后溢出了,则不会分配空间 22 } 23 } 24 } 25 else 26 { 27 xWantedSize = 0; // 加8后溢出了,则不会分配空间 28 } 29 30 // 第一次分配空间:xFreeBytesRemaining = 0x12BF0 31 if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) 32 { 33 pxPreviousBlock = &xStart; 34 pxBlock = xStart.pxNextFreeBlock; // pxBlock = 0x200001F0, pxBlock->pxNextFreeBlock = pxEnd, pxBlock->xBlockSize = 0x12BF0 35 36 // 从头开始找足够大的空闲块 37 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) 38 { 39 pxPreviousBlock = pxBlock; 40 pxBlock = pxBlock->pxNextFreeBlock; 41 } 42 43 // 已找到 44 if( pxBlock != pxEnd ) 45 { 46 // pvReturn = pxBlock + 8 = 0x200001F0 + 8 = 0x200001F8 47 pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize ); 48 // xStart.pxNextFreeBlock = pxEnd 49 pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; 50 // #define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) ) 51 // 剩下的空闲空间大于16 52 if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) 53 { 54 // pxNewBlockLink = 0x200001F0 + 0x78 = 0x20000268 55 pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); 56 // pxNewBlockLink->xBlockSize = 0x12BF0 - 0x78 = 0x12B78, pxBlock->xBlockSize = 0x78 57 pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; 58 pxBlock->xBlockSize = xWantedSize; 59 // 插入进空闲块链表 60 prvInsertBlockIntoFreeList( pxNewBlockLink ); 61 } 62 else 63 { 64 mtCOVERAGE_TEST_MARKER(); 65 } 66 // xFreeBytesRemaining = xFreeBytesRemaining - 0x78 = 0x12B78 67 xFreeBytesRemaining -= pxBlock->xBlockSize; 68 // xMinimumEverFreeBytesRemaining = 0x12BF0 69 if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) 70 { 71 xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; // xMinimumEverFreeBytesRemaining = 0x12B78 72 } 73 else 74 { 75 mtCOVERAGE_TEST_MARKER(); 76 } 77 78 pxBlock->xBlockSize |= xBlockAllocatedBit; // pxBlock->xBlockSize = 0x78 | 0x80000000 79 pxBlock->pxNextFreeBlock = NULL; 80 xNumberOfSuccessfulAllocations++; // xNumberOfSuccessfulAllocations = 1 81 } 82 else 83 { 84 mtCOVERAGE_TEST_MARKER(); 85 } 86 } 87 } 88 ( void ) xTaskResumeAll(); // 打开调度器
里面还有一个函数需要解析:prvInsertBlockIntoFreeList()
1 static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */ 2 { 3 BlockLink_t * pxIterator; 4 uint8_t * puc; 5 6 /* Iterate through the list until a block is found that has a higher address 7 * than the block being inserted. */ 8 /* 遍历列表,直到发现一个块的地址高于被插入的块的地址 */ 9 // pxBlockToInsert = 0x20000268 10 for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) 11 { 12 /* Nothing to do here, just iterate to the right position. */ 13 /* 什么都不做,只遍历 */ 14 } 15 16 /* Do the block being inserted, and the block it is being inserted after 17 * make a contiguous block of memory? */ 18 /* 被插入的块和被插入后的块是否构成一个连续的内存块? */ 19 // puc = pxIterator = &xStart 20 puc = ( uint8_t * ) pxIterator; 21 22 // puc + xStart.xBlockSize = puc + 0 = puc = &xStart 23 if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ) 24 { 25 pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; 26 pxBlockToInsert = pxIterator; 27 } 28 else 29 { 30 mtCOVERAGE_TEST_MARKER(); 31 } 32 33 /* Do the block being inserted, and the block it is being inserted before 34 * make a contiguous block of memory? */ 35 /* 被插入的块和被插入后的块是否构成一个连续的内存块? */ 36 // puc = 0x20000268 37 puc = ( uint8_t * ) pxBlockToInsert; 38 39 // puc + pxBlockToInsert->xBlockSize = puc + 0x12B78 = 0x20012DE0 = pxEnd = pxIterator->pxNextFreeBlock 40 if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock ) 41 { 42 if( pxIterator->pxNextFreeBlock != pxEnd ) 43 { 44 /* Form one big block from the two blocks. */ 45 pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; 46 pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; 47 } 48 else 49 { 50 pxBlockToInsert->pxNextFreeBlock = pxEnd; 51 } 52 } 53 else 54 { 55 pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; 56 } 57 58 /* If the block being inserted plugged a gab, so was merged with the block 59 * before and the block after, then it's pxNextFreeBlock pointer will have 60 * already been set, and should not be set here as that would make it point 61 * to itself. */ 62 /* 如果插入的块产生一个间隙,则会将相连的块合并, 63 * 那么它的pxNextFreeBlock指针已被设置,不应该在这里设置,否则会使它指向自己 */ 64 if( pxIterator != pxBlockToInsert ) 65 { 66 pxIterator->pxNextFreeBlock = pxBlockToInsert; 67 } 68 else 69 { 70 mtCOVERAGE_TEST_MARKER(); 71 } 72 }
全部执行完后,heap的示意图(首次执行pvPortMalloc):
首次执行pvPortMalloc,就分析到这里,下次分析首次释放。