FreeRTOS的堆內存管理(heap_1 ~ heap_5)


FreeRTOS的堆管理

上文對FreeRTOs的目錄結構進行了說明,其中提到了FreeRTOS\Source\portable\MemMang目錄下的五個heap_n.c文件,本文將對這個五個文件的作用、差異、使用場景進行對比,以便選擇出適合自己項目的堆管理模式。

  • FreeRTOS使用pvPortMalloc()來分配內存。
  • vPortFree()來釋放內存。

Heap_1.c

主要用於小型專一嵌入式系統。內核在任何實時任務執行之前先分配內存,一次分配永久使用並不再改變,可靠性較高。

堆的總容量 configTOTAL_HEAP_SIZE 在 FreeRTOSConfig.h 文件中配置

每創建一個任務都會分配一個堆控制塊(TCB:Task control block)和一個棧(Stack)

  • A:代表整個可分配空間
  • B:當一個任務被創建出來
  • C:當三個任務被創建出來

Heap_2.c

  • Heap_2 保留的主要目的是向后兼容,不推薦在新項目中使用。可使用Heap_4作為替代。
  • Heap_2 采用最佳適配算法,適用於需要頻繁創建和刪除需要分配固定棧內存的任務。

Heap_3.c

  • 在heap3.c中 configTOTAL_HEAP_SIZE的配置將不再生效。
  • Heap_3通過暫時掛起FreeRTOS的調度來實現malloc()和free()的線程安全。(待補充)

Heap_4.c

  • Heap_4采用首次適應算法來分配內存。heap4將相鄰未分配的內存結合成為整個大內存來減少碎片內存。

Heap5.c

  • heap_5和heap_4的使用完全一致。
  • heap_5可以對任意位置的空間進行分配,
  • heap_5在使用之前需要通過vPortDefineHeapRegions()函數進行初始化,之后才可以使用pvPortMalloc()進行內存分配。
  • PortDefineHeapRegions()的作用是明確每個分散空間的初始位置和大小。
    • 原型描述: void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );
    • 返回值結構
typedef struct HeapRegion
{
/* 內存塊的起始地址將成為堆的一部分.*/
uint8_t *pucStartAddress;
/* 堆的容量大小bytes. */
size_t xSizeInBytes;
} HeapRegion_t;

下圖表示vPortDefineHeapRegions函數的具體使用場景RAM1,RAM2,RAM3分別代表三個空閑空間

/* 圖最左側堆:A   定以RAM1-3的基本信息. */
#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 )

const HeapRegion_t xHeapRegions[] =
{
 { RAM1_START_ADDRESS, RAM1_SIZE },
 { RAM2_START_ADDRESS, RAM2_SIZE },
 { RAM3_START_ADDRESS, RAM3_SIZE },
 { NULL, 0 } /* 標志數組的結尾. */
};
int main( void )
{
 /* 初始化heap_5 */
 vPortDefineHeapRegions( xHeapRegions );
 
/* 編碼區域。*/
}
  • A僅僅展示了RAM結構,圖B 包含了堆分配的一些細節

  • 由於RAM的管理需要鏈接腳本,圖B RAM1包含了鏈接腳本,RAM2,和RAM3為空。RAM1被分為兩個區域,0x10000-0x01nnnn用來存放連接腳本,只有0x01nnnn-0x01FFFF可用,即heap5的可用空間為0x01nnnn-0x01FFFF,RAM2,RAM3。此時如果起始位置依然以0x010000作為起點將覆蓋存放變量的內存,所以必須從0x0001nnnn作為起點,可以在HeapRegion_t結構中使用xHeapRegions[] 數組作為起始地址,但由於起始地址難判定,后續結構的必要更新,堆重疊的問題此方案並不推薦。

  • 完美推薦方案C

* 定以沒有被鏈接器使用的兩個起始地址和容量 */
#define RAM2_START_ADDRESS ( ( uint8_t * ) 0x00020000 )
#define RAM2_SIZE ( 32 * 1024 )
#define RAM3_START_ADDRESS ( ( uint8_t * ) 0x00030000 )
#define RAM3_SIZE ( 32 * 1024 )
/* 定義一個數組為heap_5使用的一部分,此數組將會被鏈接器放置於RAM1 */
#define RAM1_HEAP_SIZE ( 30 * 1024 )
static uint8_t ucHeap[ RAM1_HEAP_SIZE ];
/* 定義一個數組HeapRegion_t,第一個入口只定義了ucHeap數組。像之前一樣HeapRegion_t結構定以仍需地址從小到大排列。*/
const HeapRegion_t xHeapRegions[] =
{
 { ucHeap, RAM1_HEAP_SIZE },
 { RAM2_START_ADDRESS, RAM2_SIZE },
 { RAM3_START_ADDRESS, RAM3_SIZE },
 { NULL, 0 } /* 標志數組的結束. */
};

優勢

  • 初始地址不再是常量
  • 鏈接器自動設置HeapRegion_t結構
  • 內存分配給heap_5的數據不會被鏈接器覆蓋
  • 如果ucHeap太大應用將不會鏈接

堆分配相關函數

  • size_t xPortGetFreeHeapSize( void ); 當被調用時返回堆中可用字節,可用於優化堆大小。當時用heap_3分配方案時此函數不生效。
  • size_t xPortGetMinimumEverFreeHeapSize( void ); 當調用時返回FreeRTOS應自開始運行從未存在於堆中的最小未分配字節。 可用於了解是否存在堆溢出情況。 **只可用於heap_4,heap_5堆分配方案中。
  • void vApplicationMallocFailedHook( void );
    內存分配的情況無處不在,可在應用中直接調用,此外在freeRTOS創建任務,隊列,信號量等操作時也會調用pvPortMalloc()函數。當pvPortMalloc()未找到符合大小的RAM空間時返回NULL,此時可調用回調函數vApplicationMallocFailedHook。
    • 當在FreeRTOSConfig.h配置了configUSE_MALLOC_FAILED_HOOK為1 則表示內存分配出錯時必須執行vApplicationMallocFailedHook函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM