5-1 - FreeRTOS的不同內存管理方案

FreeRTOS的內存管理

  • 近兩天從前輩的博客上看到非常好的關於freeRTOS的各項細節解析,之前一直對freeRTOS的內存管理方式有多種而感到疑惑,看了前輩文章後醍醐灌頂,在此做下記錄並表示敬意
  • FreeRTOS提供的內存分配方案分別位於不同的源文件(heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c)之中,源文件位於下載包\FreeRTOS\Source\portable\MemMang文件夾中。其它實現方法可以根據需要增加。如果要使用FreeRTOS提供的內存堆分配方案,選中的源文件必須被正確的包含到工程文件中。

heap_1.c

  • 這是所有實現中最簡單的一個。一旦分配內存之後,它甚至不允許釋放分配的內存。儘管這樣,heap_1.c還是適用於大部分嵌入式應用程序。這是因爲大多數深度嵌入式(deeplyembedded)應用只是在系統啓動時創建所有任務、隊列、信號量等,並且直到程序結束都會一直使用它們,永遠不需要刪除。
  • 當需要分配RAM時,這個內存分配方案只是簡單的將一個大數組細分出一個子集來。大數組的容量大小通過FreeRTOSConfig.h文件中的configTOTAL_HEAP_SIZE宏來設置。
  • API函數xPortGetFreeHeapSize()返回未分配的堆棧空間總大小,可以通過這個函數返回值對configTOTAL_HEAP_SIZE進行合理的設置。

heap_1功能簡介

  • 用於從不會刪除任務、隊列、信號量、互斥量等的應用程序(實際上大多數使用FreeRTOS的應用程序都符合這個條件)
    執行時間是確定的並且不會產生內存碎片。
    實現和分配過程非常簡單,需要的內存是從一個靜態數組中分配的,意味着這種內存分配通常只是適用於那些不進行動態內存分配的應用。

heap_2.c

  • 和方案1不同,這個方案使用一個最佳匹配算法,它允許釋放之前分配的內存塊。它不會把相鄰的空閒塊合成一個更大的塊(換句話說,這會造成內存碎片)。
  • 有效的堆棧空間大小由位於FreeRTOSConfig.h文件中的configTOTAL_HEAP_SIZE宏來定義。
  • API函數xPortGetFreeHeapSize()返回剩下的未分配堆棧空間的大小(可用於優化設置configTOTAL_HEAP_SIZE宏的值),但是不能提供未分配內存的碎片細節信息。

heap_2功能簡介

  • 可以用於重複的分配和刪除具有相同堆棧空間的任務、隊列、信號量、互斥量等等,並且不考慮內存碎片的應用程序。
  • 不能用在分配和釋放隨機字節堆棧空間的應用程序。
    • 如果一個應用程序動態的創建和刪除任務,並且分配給任務的堆棧空間總是同樣大小,那麼大多數情況下heap_2.c是可以使用的。但是,如果分配給任務的堆棧不總是相等,那麼釋放的有效內存可能碎片化,形成很多小的內存塊。最後會因爲沒有足夠大的連續堆棧空間而造成內存分配失敗。在這種情況下,heap_4.c是一個很好的選擇。
    • 如果一個應用程序動態的創建和刪除隊列,並且在每種情況下隊列存儲區域(隊列存儲區域指隊列項數目乘以每個隊列長度)都是同樣的,那麼大多數情況下heap_2.c可以使用。但是,如果隊列存儲區在每種情況下並不總是相等,那麼釋放的有效內存可能碎片化,形成很多小的內存塊。最後會因爲沒有足夠大的連續堆棧空間而造成內存分配失敗。在這種情況下,heap_4.c是一個很好的選擇。
    • 應用程序直接調用pvPortMalloc()vPortFree()函數,而不僅是通過FreeRTOS API間接調用。
  • 如果你的應用程序中的隊列、任務、信號量、互斥量等等處在一個不可預料的順序,則可能會導致內存碎片問題,雖然這是小概率事件,但必須牢記。
  • 不具有確定性,但是它比標準庫中的malloc函數具有高得多的效率。
    heap_2.c適用於需要動態創建任務的大多數小型實時系統(smallreal time)

heap_3.c

  • heap_3.c簡單的包裝了標準庫中的malloc()free()函數,包裝後的malloc()free()函數具備線程保護。

heap_3.c功能簡介

  • 需要鏈接器設置一個堆棧,並且編譯器庫提供malloc()free()函數。
  • 不具有確定性
  • 可能明顯的增大RTOS內核的代碼大小
    注:使用heap_3時,FreeRTOSConfig.h文件中的configTOTAL_HEAP_SIZE宏定義沒有作用

heap_4.c

  • 這個方案使用一個最佳匹配算法,但不像方案2那樣。它會將相鄰的空閒內存塊合併成一個更大的塊(包含一個合併算法)。
  • 有效的堆棧空間大小由位於FreeRTOSConfig.h文件中的configTOTAL_HEAP_SIZE來定義。
  • API函數xPortGetFreeHeapSize()返回剩下的未分配堆棧空間的大小(可用於優化設置configTOTAL_HEAP_SIZE宏的值),但是不能提供未分配內存的碎片細節信息。

heap_4.c功能簡介

  • 可用於重複分配、刪除任務、隊列、信號量、互斥量等等的應用程序。
  • 可以用於分配和釋放隨機字節內存的情況,並不像heap_2.c那樣產生嚴重碎片。
  • 不具有確定性,但是它比標準庫中的malloc函數具有高得多的效率。
    heap_4.c還特別適用於移植層代碼,可以直接使用pvPortMalloc()vPortFree()函數來分配和釋放內存

heap_5.c

  • 這個方案同樣實現了heap_4.c中的合併算法,並且允許堆棧跨越多個非連續的內存區。
  • Heap_5通過調用vPortDefineHeapRegions()函數實現初始化,在該函數執行完成前不允許使用內存分配和釋放。創建RTOS對象(任務、隊列、信號量等等)會隱含的調用pvPortMalloc(),因此必須注意:使用heap_5創建任何對象前,要先執行vPortDefineHeapRegions()函數。
  • vPortDefineHeapRegions()函數只需要單個參數。該參數是一個HeapRegion_t結構體類型數組。HeapRegion_t在portable.h中定義,如下所示:
typedef struct HeapRegion  
{  
	/* 用於內存堆的內存塊起始地址*/
	uint8_t *pucStartAddress;
	
	/* 內存塊大小 */
	size_t xSizeInBytes;
} HeapRegion_t;
  • 這個數組必須使用一個NULL指針和0字節元素作爲結束,起始地址必須從小到大排列。下面的代碼段提供一個例子。MSVCWin32模擬器演示例程使用了heap_5,因此可以當做一個參考例程。
/* 在內存中爲內存堆分配兩個內存塊.第一個內存塊0x10000字節,起始地址爲0x80000000, 
第二個內存塊0xa0000字節,起始地址爲0x90000000.起始地址爲0x80000000的內存塊的 起始地址更低,因此放到了數組的第一個位置.*/  
const HeapRegion_t xHeapRegions[] =
{
	{ ( uint8_t * ) 0x80000000UL, 0x10000 },
	{ ( uint8_t * ) 0x90000000UL, 0xa0000 },
	{ NULL, 0 } /* 數組結尾. */
};

/* 向函數vPortDefineHeapRegions()傳遞數組參數. */
vPortDefineHeapRegions( xHeapRegions );

以上對\FreeRTOS\Source\portable\MemMang文件夾下的5個內存管理方案做了較詳細的描述,想要更精準地理解內存管理可以參考官方文檔 → 傳送門Memory Management

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章