標准 C 庫中的 malloc()和 free()也可以實現動態內存管理,但是如下原因限制了其使用:
● 在小型的嵌入式系統中效率不高。
● 會占用很多的代碼空間。
● 它們不是線程安全的。
● 具有不確定性,每次執行所用的時間不同。
● 會導致內存碎片。
● 使鏈接器的配置變得復雜。
五種內存分配方案: FreeRTOS->Source->portable->MemMang
FreeRTOS 中的內存堆為 ucHeap[],大小為configTOTAL_HEAP_SIZE。
#if( configAPPLICATION_ALLOCATED_HEAP == 1 ) extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //需要用戶自行定義內存堆 #else static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //編譯器決定 #endif 當宏 configAPPLICATION_ALLOCATED_HEAP 為 1 的時候需要用戶自行定義內存堆,
否則的話由編譯器來決定,默認都是由編譯器來決定的。
如果自己定義的話就可以將內存堆定義到外部 SRAM 或者 SDRAM 中。
1、2、4、5可以通過函數 xPortGetFreeHeapSize()來獲取剩余的內存大小。
一、heap_1
#define portBYTE_ALIGNMENT 8 portmacro.h
#define portBYTE_ALIGNMENT_MASK (portBYTE_ALIGNMENT - 1) portable.h
heap_1.c
/* A few bytes might be lost to byte aligning the heap start address. */
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
static size_t xNextFreeByte = ( size_t ) 0;
void *pvPortMalloc( size_t xWantedSize ) { void *pvReturn = NULL; static uint8_t *pucAlignedHeap = NULL; /* Ensure that blocks are always aligned to the required number of bytes. */ #if( portBYTE_ALIGNMENT != 1 ) { if( xWantedSize & portBYTE_ALIGNMENT_MASK ) { /* Byte alignment required. */ 申請的時候,WantedSize向align_mask對齊(取余),WantedSize減去這個余數零頭,然后加上一個byte_align整數。比如想要9,向7取余是1,就是9-1+8=16,最后取了16個字節的空間。(8的倍數) xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); } } #endif vTaskSuspendAll(); { if( pucAlignedHeap == NULL ) { /* Ensure the heap starts on a correctly aligned boundary. */ 確保取得地址也是向 byte_align = 8字節對齊。
前邊的byte_align個字節就不要了,所以是從ucHeap[byte_align]之后的地址開始申請空間。地址最小的變動是bit3,bit012始終是0. pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); } /* Check there is enough room left for the allocation. */ if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) /*adjusted空間是去掉byte_align個字節之后的heap空間,申請的空間大小要小於總的這個adjusted空間*/ && ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */ { /* Return the next free byte then increment the index past this block. */ pvReturn = pucAlignedHeap + xNextFreeByte; xNextFreeByte += xWantedSize; } traceMALLOC( pvReturn, xWantedSize ); } ( void ) xTaskResumeAll(); #if( configUSE_MALLOC_FAILED_HOOK == 1 ) { if( pvReturn == NULL ) { extern void vApplicationMallocFailedHook( void ); vApplicationMallocFailedHook(); } } #endif return pvReturn; }
二、heap_2
heap_2提供了內存釋放函數。
heap_2不會把釋放的內存塊合並成一個大塊,這樣隨着你不斷的申請內存,內存堆就會被分為很多個大小不一的內存(塊),導致內存碎片!
讀代碼時,先看一下下面我寫的加粗的幾句話,好理解。
其實做的這個鏈表,它只管理空閑的內存塊,
但分配出去的內存塊也要帶着鏈表管理用的這個結構體,方便別人釋放之后再回到鏈表中管理。
而且可以看出,一旦內存被分配了大小,其大小是不能再更改的,所以就會導致內存碎片問題,導致程序崩潰。
1 內存塊:分配出去每塊內存,和空閑的內存,都叫內存塊。 2 但是這些內存塊大小不一,所以引入鏈表。 3 typedef struct A_BLOCK_LINK 4 { 5 struct A_BLOCK_LINK *pxNextFreeBlock; /*鏈表中下一個空閑塊 */ 6 size_t xBlockSize; /*內存塊的大小+8(要加上這兩個變量的空間)*/ 7 } BlockLink_t; 8 9 static BlockLink_t xStart, xEnd; 記錄鏈表的頭和尾 10 11 static const uint16_t heapSTRUCT_SIZE = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); 12 13 #define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) 14 15 /* A few bytes might be lost to byte aligning the heap start address. */ 16 #define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) 17 18 /* Keeps track of the number of free bytes remaining, but says nothing about 19 fragmentation. */ 20 static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE; 21 22 23 /*-----------------------------------------------------------*/ 24 static void prvHeapInit( void ) 25 { 26 BlockLink_t *pxFirstFreeBlock; 27 uint8_t *pucAlignedHeap; 28 29 /* Ensure the heap starts on a correctly aligned boundary. */ 30 pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); 31 32 /* xStart is used to hold a pointer to the first item in the list of free 33 blocks. The void cast(void*強轉) is used to prevent compiler warnings. */ 34 xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; 35 xStart.xBlockSize = ( size_t ) 0; 36 37 /* xEnd is used to mark the end of the list of free blocks. */ 38 xEnd.xBlockSize = configADJUSTED_HEAP_SIZE; 39 xEnd.pxNextFreeBlock = NULL; 40 41 /* To start with there is a single free block that is sized to take up the 42 entire heap space. */ 初始化第一個空閑內存塊 43 pxFirstFreeBlock = ( void * ) pucAlignedHeap; 44 pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE; 45 pxFirstFreeBlock->pxNextFreeBlock = &xEnd; 46 } 47 48 49 50 1. 內存塊是按大小排序的!!!鏈表的排序,並不是內存地址上是連續的!!!
2. 大小為12/14的兩個內存塊之間,插入大小為13內存塊,並不用移動內存拷貝內存,只是簡單的用鏈表把他們三個 按順序串起來管理而已。 3. 以上說的三個內存塊都是空閑內存塊,可能是被使用過后扔回來的,大小是固定的。
4. 內存管理是不對已分配的空間進行管理的(廢話!因為已經分配的空間,人家正在被用着呢,有人管理,不用這里操心!!!)
51 * Insert a block into the list of free blocks - which is ordered by size of 52 * the block. Small blocks at the start of the list and large blocks at the end 53 * of the list. 54 *-----------------------------------------------------------*/ 55 #define prvInsertBlockIntoFreeList( pxBlockToInsert ) \ 56 { \ 57 BlockLink_t *pxIterator;迭代 \ 58 size_t xBlockSize; \ 59 \ 60 xBlockSize = pxBlockToInsert->xBlockSize; \ 61 \ 62 /* Iterate through the list until a block is found that has a larger size */ \ 63 /* than the block we are inserting. */ \ 64 for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \ 65 { \ 66 /* There is nothing to do here - just iterate to the correct position. */ \ 67 } \ 68 \ 69 /* Update the list to include the block being inserted in the correct */ \ 70 /* position. */ \ 71 pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \ 72 pxIterator->pxNextFreeBlock = pxBlockToInsert; \ 73 } 74 75 76 /*-----------------------------------------------------------*/ 77 void *pvPortMalloc( size_t xWantedSize ) 78 { 79 BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; 80 static BaseType_t xHeapHasBeenInitialised = pdFALSE; 81 void *pvReturn = NULL; 82 83 vTaskSuspendAll(); 84 { 85 /* If this is the first call to malloc then the heap will require 86 initialisation to setup the list of free blocks. */ 87 if( xHeapHasBeenInitialised == pdFALSE ) 88 { 89 prvHeapInit(); 90 xHeapHasBeenInitialised = pdTRUE; 91 } 92 93 /* The wanted size is increased so it can contain a BlockLink_t 94 structure in addition to the requested amount of bytes. */ 95 if( xWantedSize > 0 ) 96 { 97 xWantedSize += heapSTRUCT_SIZE; 加上個heapStruct對byte_align取整 98 99 /* Ensure that blocks are always aligned to the required number of bytes. */ 100 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 ) 101 { 102 /* Byte alignment required. */ 103 xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); 加到一起,再取一次整。 104 } 105 } 106 107 if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) ) 108 { 109 /* Blocks are stored in byte order - traverse the list from the start 110 (smallest) block until one of adequate size is found. */ 內存塊是按大小排序的!!! 111 pxPreviousBlock = &xStart; 從最小的start內存塊開始。 112 pxBlock = xStart.pxNextFreeBlock; 113 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) 114 { 115 pxPreviousBlock = pxBlock; 116 pxBlock = pxBlock->pxNextFreeBlock; 117 } 118 119 /* If we found the end marker then a block of adequate size was not found. */ 120 if( pxBlock != &xEnd ) 上邊的遍歷,到最后都沒找到大小合適的,就失敗了。 121 { 122 /* Return the memory space - jumping over the BlockLink_t structure 123 at its start. */ 124 pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );(為什么不寫成pxBlock + heapStruct_size) 125 126 /* This block is being returned for use so must be taken out of the 127 list of free blocks. */ 128 pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; pxBlock指向的空間被踢掉了。 129 130 /* If the block is larger than required it can be split into two. */ 131 if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) 滿足WantedSize剩下的空間,比MiniBlockSize還大,就分割一下。 132 { 133 /* This block is to be split into two. Create a new block 134 following the number of bytes requested. The void cast is 135 used to prevent byte alignment warnings from the compiler. */ 136 pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); pxBlock + WantedSize之后的那個空間,割出來 137 138 /* Calculate the sizes of two blocks split from the single 139 block. */ 140 pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; 剩余空間大小 141 pxBlock->xBlockSize = xWantedSize; 分配出去的WantedSize空間大小。都已經分配出去了,還要帶着鏈表結構!為的是變成空閑空間,回來的時候,由鏈表管理!!! 142 143 /* Insert the new block into the list of free blocks. */ 144 prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); 把分割出來的空余空間,再扔回到鏈表中。 145 } 146 147 xFreeBytesRemaining -= pxBlock->xBlockSize; 全局變量改動一下。 148 } 149 } 150 151 traceMALLOC( pvReturn, xWantedSize ); 152 } 153 ( void ) xTaskResumeAll(); 154 155 #if( configUSE_MALLOC_FAILED_HOOK == 1 ) 156 { 157 if( pvReturn == NULL ) 158 { 159 extern void vApplicationMallocFailedHook( void ); 160 vApplicationMallocFailedHook(); 161 } 162 } 163 #endif 164 165 return pvReturn; 166 } 167 168 169 void vPortFree( void *pv ) 170 { 171 uint8_t *puc = ( uint8_t * ) pv; 172 BlockLink_t *pxLink; 173 174 if( pv != NULL ) 175 { 176 /* The memory being freed will have an BlockLink_t structure immediately 177 before it. */ 178 puc -= heapSTRUCT_SIZE; 一開始指向的是要釋放的空間地址,減去一個heapStruct之后,指向heapStruct這個“空閑空間鏈表”用來管理的結構體。 179 180 /* This unexpected casting is to keep some compilers from issuing 181 byte alignment warnings. */ 182 pxLink = ( void * ) puc; 183 184 vTaskSuspendAll(); 185 { 186 /* Add this block to the list of free blocks. */ 187 prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); 扔回到空閑空間鏈表 188 xFreeBytesRemaining += pxLink->xBlockSize; 189 traceFREE( pv, pxLink->xBlockSize ); 190 } 191 ( void ) xTaskResumeAll(); 192 } 193 } 194 /*-----------------------------------------------------------*/ 195 196 size_t xPortGetFreeHeapSize( void ) 197 { 198 return xFreeBytesRemaining; 199 }
三、heap_3
這個分配方法是對標准 C 中的函數 malloc()和 free()的簡單封裝, FreeRTOS 對這兩個函數做了線程保護。
#include <stdlib.h> void *pvPortMalloc( size_t xWantedSize ) { void *pvReturn; vTaskSuspendAll(); { pvReturn = malloc( xWantedSize ); traceMALLOC( pvReturn, xWantedSize ); } ( void ) xTaskResumeAll(); #if( configUSE_MALLOC_FAILED_HOOK == 1 ) { if( pvReturn == NULL ) { extern void vApplicationMallocFailedHook( void ); vApplicationMallocFailedHook(); } } #endif return pvReturn; } /*-----------------------------------------------------------*/ void vPortFree( void *pv ) { if( pv ) { vTaskSuspendAll(); { free( pv ); traceFREE( pv, 0 ); } ( void ) xTaskResumeAll(); } }
1、需要編譯器提供一個內存堆,STM32 可以修改啟動文件中的 Heap_Size。編譯器庫要提供 malloc()和 free()函數。
2、具有不確定性
3、可能會增加代碼量。
注意,在 heap_3 中 configTOTAL_HEAP_SIZE 是沒用的!
四、heap_4
最優的匹配算法?在哪兒?
內存塊合並算法,倒是看到了。
Insert函數:內存塊的排序不再使用blockSize的大小進行排序,而是使用內存塊的地址排序(方便內存碎片合並!)
static void prvHeapInit( void ) { BlockLink_t *pxFirstFreeBlock; uint8_t *pucAlignedHeap; size_t uxAddress; size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; /* Ensure the heap starts on a correctly aligned boundary. */ //起始地址 做字節對齊 uxAddress = ( size_t ) ucHeap; if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) { uxAddress += ( portBYTE_ALIGNMENT - 1 ); uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); xTotalHeapSize -= uxAddress - ( size_t ) ucHeap; } pucAlignedHeap = ( uint8_t * ) uxAddress; /* xStart is used to hold a pointer to the first item in the list of free blocks. The void cast is used to prevent compiler warnings. */ xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; xStart.xBlockSize = ( size_t ) 0; /* pxEnd is used to mark the end of the list of free blocks and is inserted at the end of the heap space. */ uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize; uxAddress -= xHeapStructSize; uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); pxEnd = ( void * ) uxAddress; //不同於heap2,這里的End節點放到了空閑空間的最后 pxEnd->xBlockSize = 0; pxEnd->pxNextFreeBlock = NULL; /* To start with there is a single free block that is sized to take up the entire heap space, minus the space taken by pxEnd. */ pxFirstFreeBlock = ( void * ) pucAlignedHeap; pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock; pxFirstFreeBlock->pxNextFreeBlock = pxEnd; /* Only one block exists - and it covers the entire usable heap space. */ xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; //最小的那個空閑內存塊 xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; /* Work out the position of the top bit in a size_t variable. */ //size_t最高位置1,BlockLink_t.BlockSize本來是描述內存塊大小的 //這里將其最高位用來描述這個內存塊是否被使用了,所以heap4中的內存塊最大只能是0x7FFF_FFFF xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); }
處理碎片的秘訣:
1 static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ) 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 //這里的排序,比較的是地址,不再是blockSize 9 for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) 10 { 11 /* Nothing to do here, just iterate to the right position. */ 12 } 13 14 /* Do the block being inserted, and the block it is being inserted after 15 make a contiguous block of memory? */ 16 //要插回來的內存塊B的起始地址,剛好是"被B插到后邊的A"的結束地址,所以這兩塊可以合並!!! 17 //因為鏈表在內存上是不連續的,A的地址比B的地址小,並不一定他們兩個就是地址上連續的。 18 puc = ( uint8_t * ) pxIterator; 19 if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ) 20 { 21 pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; 22 //AB合並,BlockToInsert指向A的起始地址 23 pxBlockToInsert = pxIterator; 24 } 25 else 26 { 27 mtCOVERAGE_TEST_MARKER(); 28 } 29 30 /* Do the block being inserted, and the block it is being inserted before 31 make a contiguous block of memory? */ 32 //注意區分這里和上邊的,before/after 33 //要插回來的B的結束地址,剛好是"被B插到前邊的C”的起始地址,可以合並!!! 34 puc = ( uint8_t * ) pxBlockToInsert; 35 if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock ) 36 { 37 if( pxIterator->pxNextFreeBlock != pxEnd ) //不是End節點 38 { 39 /* Form one big block from the two blocks. */ //合並 40 pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; 41 pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; 42 } 43 else 44 { 45 pxBlockToInsert->pxNextFreeBlock = pxEnd; //C是End節點,那不能合並了 46 } 47 } 48 else 49 { 50 pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; //不合並的處理 51 } 52 53 /* If the block being inserted plugged a gab, so was merged with the block 54 before and the block after, then it's pxNextFreeBlock pointer will have 55 already been set, and should not be set here as that would make it point 56 to itself. */ 57 if( pxIterator != pxBlockToInsert ) //兩次合並都沒進行 58 { 59 pxIterator->pxNextFreeBlock = pxBlockToInsert; 60 } 61 else 62 { 63 mtCOVERAGE_TEST_MARKER(); 64 } 65 }
1 void *pvPortMalloc( size_t xWantedSize ) 2 { 3 BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; 4 void *pvReturn = NULL; 5 6 vTaskSuspendAll(); 7 { 8 /* If this is the first call to malloc then the heap will require 9 initialisation to setup the list of free blocks. */ 10 if( pxEnd == NULL ) 11 { 12 prvHeapInit(); 13 } 14 else 15 { 16 mtCOVERAGE_TEST_MARKER(); 17 } 18 19 /* Check the requested block size is not so large that the top bit is 20 set. The top bit of the block size member of the BlockLink_t structure 21 is used to determine who owns the block - the application or the 22 kernel, so it must be free. */ 23 //內存塊大小最大是0x7FFF_FFFF 24 if( ( xWantedSize & xBlockAllocatedBit ) == 0 ) 25 { 26 /* The wanted size is increased so it can contain a BlockLink_t 27 structure in addition to the requested amount of bytes. */ 28 if( xWantedSize > 0 ) 29 { 30 xWantedSize += xHeapStructSize; //加上個heapStruct對byte_align取整 31 /* Ensure that blocks are always aligned to the required number 32 of bytes. */ 33 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) 34 { 35 /* Byte alignment required. */ 36 xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); //加到一起,再一次取整 !!!見后!!! 37 configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 ); //如果沒有向byte_align個字節對齊,就報錯了。 38 } 39 else 40 { 41 mtCOVERAGE_TEST_MARKER(); 42 } 43 } 44 else 45 { 46 mtCOVERAGE_TEST_MARKER(); 47 } 48 49 if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) 50 { 51 /* Traverse the list from the start (lowest address) block until 52 one of adequate size is found. */ 53 //雖然鏈表已經不按大小排序了,但是按大小找個合適的內存塊,也說得過去 54 pxPreviousBlock = &xStart; 55 pxBlock = xStart.pxNextFreeBlock; 56 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) 57 { 58 pxPreviousBlock = pxBlock; 59 pxBlock = pxBlock->pxNextFreeBlock; 60 } 61 62 /* If the end marker was reached then a block of adequate size 63 was not found. */ 64 if( pxBlock != pxEnd ) 65 { 66 /* Return the memory space pointed to - jumping over the 67 BlockLink_t structure at its start. */ 68 pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize ); 69 70 /* This block is being returned for use so must be taken out 71 of the list of free blocks. */ 72 pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; //從鏈表踢掉 73 74 /* If the block is larger than required it can be split into 75 two. */ 76 if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) //可以分割 77 { 78 /* This block is to be split into two. Create a new 79 block following the number of bytes requested. The void 80 cast is used to prevent byte alignment warnings from the 81 compiler. */ 82 pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); 83 configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 ); //沒有向byte_align對齊,就報錯 84 85 /* Calculate the sizes of two blocks split from the 86 single block. */ 87 pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; 88 pxBlock->xBlockSize = xWantedSize; 89 90 /* Insert the new block into the list of free blocks. */ 91 prvInsertBlockIntoFreeList( pxNewBlockLink ); //分割出來的扔回鏈表 92 } 93 else 94 { 95 mtCOVERAGE_TEST_MARKER(); 96 } 97 98 xFreeBytesRemaining -= pxBlock->xBlockSize; //更新剩余空間變量 99 100 if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) 101 { 102 xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; //更新最小內存塊的大小 103 } 104 else 105 { 106 mtCOVERAGE_TEST_MARKER(); 107 } 108 109 /* The block is being returned - it is allocated and owned 110 by the application and has no "next" block. */ 111 pxBlock->xBlockSize |= xBlockAllocatedBit; //置1表示找個內存塊被占用了 112 pxBlock->pxNextFreeBlock = NULL; 113 } 114 else 115 { 116 mtCOVERAGE_TEST_MARKER(); 117 } 118 } 119 else 120 { 121 mtCOVERAGE_TEST_MARKER(); 122 } 123 } 124 else 125 { 126 mtCOVERAGE_TEST_MARKER(); 127 } 128 129 traceMALLOC( pvReturn, xWantedSize ); 130 } 131 ( void ) xTaskResumeAll(); 132 133 #if( configUSE_MALLOC_FAILED_HOOK == 1 ) 134 { 135 if( pvReturn == NULL ) 136 { 137 extern void vApplicationMallocFailedHook( void ); 138 vApplicationMallocFailedHook(); 139 } 140 else 141 { 142 mtCOVERAGE_TEST_MARKER(); 143 } 144 } 145 #endif 146 147 configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 ); //沒有向byte_align對齊,就報錯。 148 return pvReturn; 149 }
!!!處【原子資料分析】
xWantedSize為0x7FFFFFFF,那么 xWantedSize 加上結構體BlockLink_t 的大小就是0x7FFFFFFF + 8 = 0x80000007,
再做一次 8 字節對齊 xWantedSize 就是 0x80000008,其最高位為1。
前面已經說了, BlockLink_t 中的變量 xBlockSize 的最高位是用來標記內存塊是否被使用的,這里明顯沖突了,但是 FreeRTOS 對此並沒有做處理。
(那么能分配的最大空間值,應該是0x7FFF_FFFF - 8 = 0x7FFFFFF7了?我猜的。。。)
void vPortFree( void *pv ) { uint8_t *puc = ( uint8_t * ) pv; BlockLink_t *pxLink; if( pv != NULL ) { /* The memory being freed will have an BlockLink_t structure immediately before it. */ puc -= xHeapStructSize; //指向鏈表結構體 /* This casting is to keep the compiler from issuing warnings. */ pxLink = ( void * ) puc; /* Check the block is actually allocated. */ configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); //最高位是1 configASSERT( pxLink->pxNextFreeBlock == NULL ); //是被使用的內存塊(分配的時候NextFreeBlock被設置為NULL) if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) { if( pxLink->pxNextFreeBlock == NULL ) { /* The block is being returned to the heap - it is no longer allocated. */ pxLink->xBlockSize &= ~xBlockAllocatedBit; //清零標志,0x8000_0008變成0x0000_0008 vTaskSuspendAll(); { /* Add this block to the list of free blocks. */ xFreeBytesRemaining += pxLink->xBlockSize; //更新剩余空間變量,最小空間變量咋沒更新 traceFREE( pv, pxLink->xBlockSize ); prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); //扔回 } ( void ) xTaskResumeAll(); } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } } }
五、heap5
heap_5 允許內存堆跨越多個不連續的內存段。
STM32 可以外接 SRAM 甚至大容量的 SDRAM,如果使用 heap_4 的話你就只能在內部 RAM 和外部SRAM 或 SDRAM 之間二選一了,
使用 heap_5 的話就不存在這個問題,兩個都可以一起作為內存堆來用。
如果使用 heap_5 的話,在調用 API 函數之前需要先調用函數 vPortDefineHeapRegions ()來對內存堆做初始化處理。
函數 vPortDefineHeapRegions()有一個參數,參數是一個 HeapRegion_t 類型的數組,
HeapRegion 為一個結構體,此結構體在portable.h 中有定義,
typedef struct HeapRegion { uint8_t *pucStartAddress; //內存塊的起始地址 size_t xSizeInBytes; //內存段大小 } HeapRegion_t;
現在有三個內存段: CCM、內部 SRAM、外部 SDRAM,
起始分別為: 0X10000000、 0X20000000、 0XC0000000,
大小分別為: 64KB、192KB、 32MB,那么數組就如下:
HeapRegion_t xHeapRegions[] = { { ( uint8_t * ) 0X10000000UL, 0x10000 }, //CCM 內存,起始地址 0X10000000,大小 64KB { ( uint8_t * ) 0X20000000UL, 0x30000 },//內部 SRAM 內存,起始地址 0X20000000,大小為 192KB { ( uint8_t * ) 0XC0000000UL, 0x2000000},//外部 SDRAM 內存,起始地址 0XC0000000,大小為 32MB { NULL, 0 } //數組結尾 };
注意,數組中成員順序按照地址從低到高的順序排列,而且最后一個成員必須使用 NULL。
heap_5 允許內存堆不連續,說白了就是允許有多個內存堆。
在 heap_2 和 heap_4 中只有一個內存堆,初始化的時候只也只需要處理一個內存堆。
heap_5 有多個內存堆,這些內存堆會被連接在一起,和空閑內存塊鏈表類似,這個處理過程由函數 vPortDefineHeapRegions()完成。
1 void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) 2 { 3 BlockLink_t *pxFirstFreeBlockInRegion = NULL, *pxPreviousFreeBlock; 4 size_t xAlignedHeap; 5 size_t xTotalRegionSize, xTotalHeapSize = 0; 6 BaseType_t xDefinedRegions = 0; 7 size_t xAddress; 8 const HeapRegion_t *pxHeapRegion; 9 10 /* Can only call once! */ 11 configASSERT( pxEnd == NULL ); 12 13 pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] ); 14 15 while( pxHeapRegion->xSizeInBytes > 0 ) 16 { 17 xTotalRegionSize = pxHeapRegion->xSizeInBytes; 18 19 /* Ensure the heap region starts on a correctly aligned boundary. */ 20 xAddress = ( size_t ) pxHeapRegion->pucStartAddress; 21 if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) 22 { 23 xAddress += ( portBYTE_ALIGNMENT - 1 ); 24 xAddress &= ~portBYTE_ALIGNMENT_MASK; 25 26 /* Adjust the size for the bytes lost to alignment. */ 27 xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress; 28 } 29 30 xAlignedHeap = xAddress; 31 32 /* Set xStart if it has not already been set. */ 33 if( xDefinedRegions == 0 ) 34 { 35 /* xStart is used to hold a pointer to the first item in the list of 36 free blocks. The void cast is used to prevent compiler warnings. */ 37 xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap; 38 xStart.xBlockSize = ( size_t ) 0; 39 } 40 else 41 { 42 /* Should only get here if one region has already been added to the 43 heap. */ 44 configASSERT( pxEnd != NULL ); 45 46 /* Check blocks are passed in with increasing start addresses. */ 47 configASSERT( xAddress > ( size_t ) pxEnd ); 48 } 49 50 /* Remember the location of the end marker in the previous region, if 51 any. */ 52 pxPreviousFreeBlock = pxEnd; 53 54 /* pxEnd is used to mark the end of the list of free blocks and is 55 inserted at the end of the region space. */ 56 xAddress = xAlignedHeap + xTotalRegionSize; 57 xAddress -= xHeapStructSize; 58 xAddress &= ~portBYTE_ALIGNMENT_MASK; 59 pxEnd = ( BlockLink_t * ) xAddress; 60 pxEnd->xBlockSize = 0; 61 pxEnd->pxNextFreeBlock = NULL; 62 63 /* To start with there is a single free block in this region that is 64 sized to take up the entire heap region minus the space taken by the 65 free block structure. */ 66 pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap; 67 pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion; 68 pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd; 69 70 /* If this is not the first region that makes up the entire heap space 71 then link the previous region to this region. */ 72 if( pxPreviousFreeBlock != NULL ) 73 { 74 pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion; 75 } 76 77 xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize; 78 79 /* Move onto the next HeapRegion_t structure. */ 80 xDefinedRegions++; 81 pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] ); 82 } 83 84 xMinimumEverFreeBytesRemaining = xTotalHeapSize; 85 xFreeBytesRemaining = xTotalHeapSize; 86 87 /* Check something was actually defined before it is accessed. */ 88 configASSERT( xTotalHeapSize ); 89 90 /* Work out the position of the top bit in a size_t variable. */ 91 xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); 92 }
留白