本文由嵌入式企鵝圈原創團隊成員朱衡德(Hunter_Zhu)供稿.html
輕量級操做系統FreeRTOS的內存管理機制(二)中講到,heap2.c的內存管理機制會致使內存碎片的問題,系統運行久後會出現沒法分配大塊內存的狀況,heap4.c中的管理機制提供瞭解決方法,它是在heap2.c的基礎上添加了地址相鄰空閒塊間合併的功能,而heap5.c是對heap4.c的進一步擴展,它可以支持多塊不連續分佈的RAM空間做爲堆使用,本篇將對heap4.c、heap5.c中的管理機制進行分析。算法
1、heap4.c數組
1.重要結構體函數
2.重要變量spa
xBlockAllocatedBit在堆初始化時被賦值爲:操作系統
即最高位爲1,在分配內存的時候xBlockSize將和置位變量xBlockAllocatedBit相與,使得xBlockSize最高位置位,表示改內存塊屬於應用程序;在回收內存時,經過xBlockSize &= ~xBlockAllocatedBit操做,將xBlockSize 最高位清零表示該內存空間未分配,xBlockSize 最高位可用於判斷該內存當前是應用程序仍是操做系統管理。3d
3.堆初始化指針
prvHeapInit(void)函數用於初始化堆空間,在第一次內存分配前須要被調用,主要完成了幾個工做:htm
1)根據字節對齊劃分有效的可分配空間;blog
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)在插入鏈表過程當中,判斷插入位置的先後結點的空閒塊是否和回收的內存空間在地址上連續,若是是,就進行合併。
如下圖例是一個合併的過程:
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中的內存分配、內存回收的思想。