本文由嵌入式企鵝圈原創團隊成員朱衡德(Hunter_Zhu)供稿。
輕量級操作系統FreeRTOS的內存管理機制(二)中講到,heap2.c的內存管理機制會導致內存碎片的問題,系統運行久後會出現無法分配大塊內存的情況,heap4.c中的管理機制提供瞭解決方法,它是在heap2.c的基礎上添加了地址相鄰空閒塊間合併的功能,而heap5.c是對heap4.c的進一步擴展,它能夠支持多塊不連續分佈的RAM空間作爲堆使用,本篇將對heap4.c、heap5.c中的管理機制進行分析。
一、heap4.c
1.重要結構體
2.重要變量
xBlockAllocatedBit在堆初始化時被賦值爲:
即最高位爲1,在分配內存的時候xBlockSize將和置位變量xBlockAllocatedBit相與,使得xBlockSize最高位置位,表示改內存塊屬於應用程序;在回收內存時,通過xBlockSize &= ~xBlockAllocatedBit操作,將xBlockSize 最高位清零表示該內存空間未分配,xBlockSize 最高位可用於判斷該內存當前是應用程序還是操作系統管理。
3.堆初始化
prvHeapInit(void)函數用於初始化堆空間,在第一次內存分配前需要被調用,主要完成了幾個工作:
1)根據字節對齊劃分有效的可分配空間;
2)初始化鏈表結構;
初始化後的鏈表結構和內存空間:
在heap4.c方案中,鏈表結點是按照空閒塊地址由低到高排序的。上圖中,xStart是表頭結點,pEnd是表尾結點指針,鏈表的表尾結點在堆中進行分配。
4.內存分配機制
heap4.c內存分配策略:空閒塊通過鏈表結構進行管理,當需要分配一塊內存時,遍歷鏈表,找到第一塊夠大的空閒塊進行分配,從鏈表中移除出來,同時檢查這塊空間是否過大,如果過大就將空閒塊進行切割,然後將剩下部分重新用BlockLink_t描述並插入到鏈表中。
和heap2.c有所不同的是,當找到了合適的空閒塊後,BlockLink_t中的成員xBlockSize的最高位會置位爲1,以表示該空間屬於應用程序:
因此,請求分配的空間大小xWantedSize不能大到最高位爲1。
5.內存回收
在內存回收過程中,vPortFree函數會讀取pv所指空間的BlockLink_t結構體,首先判斷成員xBlockSize最高位是否爲1(即該空間原屬於應用程序),如果滿足才進行內存回收。
在heap4.c中最大的特點是在回收內存時添加了相鄰空閒塊的合併的算法,函數prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )將空閒塊插入鏈表中(由vPortFree()調用),和heap2.c對比,所不同的有兩點:
1)heap4.c的鏈表是按照地址低到高排序的,heap2.c的鏈表是根據空閒塊大小排序的;
2)在插入鏈表過程中,判斷插入位置的前後結點的空閒塊是否和回收的內存空間在地址上連續,如果是,就進行合併。
以下圖例是一個合併的過程:
二、heap5.c
heap5.c是對heap4.c的進一步拓展,heap5.c能夠支持多塊不連續的RAM空間作爲內存分配空間,內存分配策略和回收機制和heap4.c一樣。
heap5.c中定義了一個重要結構體:
這個結構體描述用作堆空間的一塊內存的信息,記錄了該內存塊的起始地址和大小。多塊不連續的內存使用一個HeapRegion_t[]數組記錄,並且約定數組以{NULL, 0}元素結尾,其作爲vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )函數的參數,進行堆初始化。
在FreeRTOS中的WIN32-MSVC示例是採用heap5.c的內存分配策略,HeapRegion_t[]示例如下:
經過初始化後的堆空間和鏈表結構如下圖所示:
vPortDefineHeapRegions()函數將HeapRegion_t[]數組中不連續的RAM空間連接起來:通過在一塊內存末尾存儲一個BlockLink_t結構體,其pxNextBlock成員指向下一塊內存中的第一塊空閒塊以建立起連接。
heap5.c較heap4.c而言主要增加了這部分功能,保留了heap4.c中的內存分配、內存回收的思想。
關注微信公衆號:嵌入式企鵝圈,獲得上百篇物聯網原創技術分享!