FreeRTOS --(6)記憶體管理 heap5
轉載自https://blog.csdn.net/zhoutaopower/article/details/106748308
FreeRTOS 中的 heap 5 記憶體管理,相對於 heap 4《FreeRTOS --(5)記憶體管理 heap4》只增加了對非連續記憶體區域的管理,什麼叫非連續區域記憶體呢?比如一款晶片,它即支援了內部的 RAM,也支援了外掛 RAM,那麼這兩個記憶體就可能在地址上不是連續的,比如 RAM1、RAM2、RAM3,如下所示:
針對這種情況,就可以使用 heap 5 來管理;
不同於之前的 heap 管理,heap 5 引入了一個結構體來管理這些非連續的區域:
typedef structHeapRegion { /* The start address of a block of memory that will be part of the heap.*/ uint8_t *pucStartAddress; /* The size of the block of memory in bytes. */ size_t xSizeInBytes; } HeapRegion_t;
一個塊連續的記憶體用一個HeapRegion_t表示,多個連續記憶體通過HeapRegion_t陣列的形式組織成為了所有的記憶體;
heap 5 要求,在呼叫正式的記憶體分配函式之前,需要定義 HeapRegion,並呼叫vPortDefineHeapRegions來初始化它;
官方給了一個 demo:
/* Define the start address and size of the three RAM regions. */ #define RAM1_START_ADDRESS ( ( uint8_t * ) 0x00010000 ) #define RAM1_SIZE ( 65 * 1024 ) #define RAM2_START_ADDRESS ( ( uint8_t * ) 0x00020000 ) #define RAM2_SIZE ( 32 * 1024 ) #define RAM3_START_ADDRESS ( ( uint8_t * ) 0x00030000 ) #define RAM3_SIZE ( 32 * 1024 ) /*Create an array of HeapRegion_t definitions, with an index for each of the three RAM regions, and terminating the array with a NULL address. The HeapRegion_t structures must appear in start address order, with the structure that contains the lowest start address appearing first. */ const HeapRegion_t xHeapRegions[] = { { RAM1_START_ADDRESS, RAM1_SIZE }, { RAM2_START_ADDRESS, RAM2_SIZE }, { RAM3_START_ADDRESS, RAM3_SIZE }, { NULL, 0 } /* Marks the end of the array. */ }; int main( void ) { /* Initialize heap_5. */ vPortDefineHeapRegions( xHeapRegions ); /* Add application code here. */ }
如果有 3 個 RAM 區域的話,那麼這樣去定義他們;
需要注意的幾點是:
1、定義HeapRegion_t陣列的時候,最後一定要定義成為 NULL 和 0,這樣接口才知道這是終點;
2、被定義的 RAM 區域,都會去參與記憶體管理;
那麼問題就來了,在真實使用的時候,有可能你很難去定義部分 RAM 的 Start Address!
比如,一款晶片,它告訴你,它的第一塊 RAM 有 64KB,第二塊 RAM 有 32KB,第三塊 RAM 有 32KB,那麼你顯然不能夠直接將這些記憶體資訊,按照上面 demo 程式碼的形式,定義到這個表格中,因為在編譯階段,有可能你相關的程式碼和資料等等(.text,.data,.bss,等)都會佔用一部分的 RAM,但凡是定義到這個HeapRegion_t陣列表格的,都會參與記憶體管理的行為,這顯然是我們不願意的;
也就是說,比如你晶片的 RAM 起始地址 0x2000_0000,你編譯你的 Source Code 後,相關的程式碼和資料要佔用 20KB,也就是 0x5000;那麼你定義的 RAM1_START_ADDRESS 起始地址,就必須要大於0x2000_0000+0x5000,這樣才不會踩到你的其他資料;但是呢?你總不可能每次編譯完都去改你的這個表吧?這是很痛苦的事情;
所有考慮到實際的使用,官方給出參考 demo 的方法是:
/* Define the start address and size of the two RAM regions not used by the linker. */ #define RAM2_START_ADDRESS ( ( uint8_t * ) 0x00020000 ) #define RAM2_SIZE ( 32 * 1024 ) #define RAM3_START_ADDRESS ( ( uint8_t * ) 0x00030000 ) #define RAM3_SIZE ( 32 * 1024 ) /* Declare an array that will be part of the heap used by heap_5. The array will be placed in RAM1 by the linker. */ #define RAM1_HEAP_SIZE ( 30 * 1024 ) static uint8_t ucHeap[ RAM1_HEAP_SIZE ]; /* Create an array of HeapRegion_t definitions. Whereas in Listing 6 the first entry described all of RAM1, so heap_5 will have used all of RAM1, this time the first entry only describes the ucHeap array, so heap_5 will only use the part of RAM1 that contains the ucHeap array. The HeapRegion_t structures must still appear in start address order, with the structure that contains the lowest start address appearing first. */ const HeapRegion_t xHeapRegions[] = { { ucHeap, RAM1_HEAP_SIZE }, { RAM2_START_ADDRESS, RAM2_SIZE }, { RAM3_START_ADDRESS, RAM3_SIZE }, { NULL, 0 } /* Marks the end of the array. */ };
即,將連結的程式碼資料,根據連結器(Linker)配置後,這些都放置在第一段的區域,ucHeap 也放在一樣的地方,這樣就避免去根據 map 檔案去硬編碼這個表格;
通過 beyond compare 可以知道,heap 5 和 heap 4 的程式碼在分配記憶體的pvPortMalloc,和釋放記憶體的vPortFree,以及插入節點合併空閒記憶體 prvInsertBlockIntoFreeList 的部分,幾乎完全一樣,唯一不一樣的地方在於:
heap 4 的記憶體初始化用的是prvHeapInit
heap 5的記憶體初始化用的是vPortDefineHeapRegions
那我們就來看看這個vPortDefineHeapRegions 的實現:
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) { BlockLink_t *pxFirstFreeBlockInRegion = NULL, *pxPreviousFreeBlock; size_t xAlignedHeap; size_t xTotalRegionSize, xTotalHeapSize = 0; BaseType_t xDefinedRegions = 0; size_t xAddress; const HeapRegion_t *pxHeapRegion; /* Can only call once! */ configASSERT( pxEnd == NULL ); pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] ); while( pxHeapRegion->xSizeInBytes > 0 ) { xTotalRegionSize = pxHeapRegion->xSizeInBytes; /* Ensure the heap region starts on a correctly aligned boundary. */ xAddress = ( size_t ) pxHeapRegion->pucStartAddress; if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) { xAddress += ( portBYTE_ALIGNMENT - 1 ); xAddress &= ~portBYTE_ALIGNMENT_MASK; /* Adjust the size for the bytes lost to alignment. */ xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress; } xAlignedHeap = xAddress; /* Set xStart if it has not already been set. */ if( xDefinedRegions == 0 ) { /* 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 = ( BlockLink_t * ) xAlignedHeap; xStart.xBlockSize = ( size_t ) 0; } else { /* Should only get here if one region has already been added to the heap. */ configASSERT( pxEnd != NULL ); /* Check blocks are passed in with increasing start addresses. */ configASSERT( xAddress > ( size_t ) pxEnd ); } /* Remember the location of the end marker in the previous region, if any. */ pxPreviousFreeBlock = pxEnd; /* pxEnd is used to mark the end of the list of free blocks and is inserted at the end of the region space. */ xAddress = xAlignedHeap + xTotalRegionSize; xAddress -= xHeapStructSize; xAddress &= ~portBYTE_ALIGNMENT_MASK; pxEnd = ( BlockLink_t * ) xAddress; pxEnd->xBlockSize = 0; pxEnd->pxNextFreeBlock = NULL; /* To start with there is a single free block in this region that is sized to take up the entire heap region minus the space taken by the free block structure. */ pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap; pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion; pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd; /* If this is not the first region that makes up the entire heap space then link the previous region to this region. */ if( pxPreviousFreeBlock != NULL ) { pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion; } xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize; /* Move onto the next HeapRegion_t structure. */ xDefinedRegions++; pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] ); } xMinimumEverFreeBytesRemaining = xTotalHeapSize; xFreeBytesRemaining = xTotalHeapSize; /* Check something was actually defined before it is accessed. */ configASSERT( xTotalHeapSize ); /* Work out the position of the top bit in a size_t variable. */ xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); }
經過一些對齊操作,將 demo 中的 3 塊記憶體通過連結串列的方式掛接起來了,只不過記憶體地址不連續而已,但是對於連續的記憶體地址中,照樣在釋放的時候,可以進行合併操作;