FreeRTOS--堆內存管理

由於項目須要,最近開始學習FreeRTOS,一開始有些緊張,由於兩個星期以前對於FreeRTOS的熟悉度幾乎爲零,通過對FreeRTOS官網的例子程序的摸索,和項目中問題的解決,遇到了不少熟悉的身影,之前在Linux平臺編程的經歷給了我一些十分有用的經驗,後悔當初沒能在第一家公司待下去,浪費了大好時光。好吧,如今仍是潛下心來搞搞FreeRTOS吧。html

後續都是一系列FreeRTOS相關的隨筆,先把FreeRTOS「聖經」--Mastering the FreeRTOS Real Time kernel -- A Hands On Tutorial Guide 20161204好好研讀,接連的幾個隨筆都是我從這本「聖經」中翻譯出來的。翻譯不免有所疏漏、詞不達意,你們湊合着看吧。算法

從FreeRTOS V9.0.0開始FreeRTOS應用程序能夠徹底用靜態分配內存,而沒有必要引入堆內存管理。編程

章節引言和範圍

前提

FreeRTOS是以C源文件的形式提供的,所以成爲一名合格的C語言編程人員是使用FreeRTOS的必要條件,於是這個章節假定讀者熟悉如下概念:api

  • C語言項目是如何構建的,包含不一樣的編譯和連接過程
  • 堆和棧分別是什麼
  • 標準C庫的malloc()free()函數

動態內存分配以及它和FreeRTOS的關係

從FreeRTOS V9.0.0開始內核對象既能夠在編譯的時候靜態分配,也能夠在運行時動態分配。本書隨後的章節將會介紹如下內核對象:tasks, queues, semaphoresevent groups。爲了儘量讓FreeRTOS易於使用,這些內核對象並非在編譯時靜態分配的,而是在運行時動態分配的。內核對象建立時FreeRTOS分配RAM而在內核對象刪除時釋放內存。這樣的策略減小了設計和計劃上的努力,簡化了API,而且減小了RAM的佔用。數組

動態內存分配是C語言編程的概念,而不是針對FreeRTOS或者多任務編程的概念。它和FreeRTOS是相關的,由於內核對象是動態分配的,而且通用編譯器提供的動態內存分配方案對於實時應用程序並不老是適合的。安全

內存可使用標準C庫的malloc()free()函數來分配,但有可能不適合,或者恰當,由於下幾點緣由:ide

  • 在小型嵌入式系統中並不老是可用的
  • 它們的實現可能很是的大,佔據了至關大的一塊代碼空間
  • 他們幾乎都不是線程安全的
  • 它們並非肯定的,每次調用這些函數執行的時間可能都不同
  • 它們有可能產生碎片
  • 它們有可能打亂連接器的配置
  • 若是容許堆空間的生長方向覆蓋其餘變量佔據的內存,它們會成爲debug的災難

動態內存分配的可選項

從FreeRTOS V9.0.0開始內核對象既能夠在編譯時靜態分配也能夠在運行時動態分配。現在FreeRTOS把內存分配放在可移植層。這是認識到不一樣的嵌入式操做有不一樣的動態內存管理方法和時間要求,所以單個的動態內存分配算法將只適合於應用程序的一個子集。一樣,從核心代碼庫中移除動態內存分配使得應用程序編寫者提供本身的特定的實現,若是適合的話。函數

當FreeRTOS須要RAM的時候,並非調用malloc(),而是調用pvPortMalloc()。當須要釋放RAM的時候,並非調用free(),而是調用vPortFree()pvPortMalloc()和標準C庫的malloc()有一樣的函數原型,vPortFree()和標準C庫的free()有一樣的函數原型。學習

pvPortMalloc()vPortFree()都是公共函數,所以可以被應用代碼調用。優化

FreeRTOS對於pvPortMalloc()vPortFree()提供了5種實現,後續章節會講到。FreeRTOS應用程序可使用其中的一種,或者使用本身的實現。5種實現分別在heap_1.c, heap_2.c, heap_3.c, heap_4.cheap_5.c文件中,都存在於文件夾 FreeRTOS/Source/portable/MemMang 下。

範圍

本章節致力於讓讀者深刻理解:

  • FreeRTOS什麼時候分配RAM
  • FreeRTOS 提供的5種內存分配方案
  • 選用哪種內存分配方案

內存分配方案示例

Heap_4 (其餘幾種暫不去了解)

heap_1, heap_2 同樣,heap_4也是把數組切割成更小的塊。和前面同樣,數組是靜態聲明的,由宏configTOTAL_HEAP_SIZE指定大小,因此這就使得即使數組中的內存尚未被分配出去就讓應用程序顯得消耗了大量的RAM。

Heap_4使用了最早適應算法來分配內存。和heap_2不一樣,heap_4把臨近的空閒的存儲空間拼湊成一個更大的內存塊,這就減小了內存碎片化的風險。

最早適應算法確保了pvPortMalloc()使用第一塊空閒的足夠大的內存來知足要申請的字節數。考慮下面的情景:

  • 堆裏有3塊空閒內存塊,它們的大小分別是5個字節,200個字節,100個字節
  • 調用pvPortMalloc()來申請20個字節的RAM
    知足字節數要求的第一塊空閒RAM塊是200個字節的RAM塊,所以pvPortMalloc()把大小爲200個字節的RAM塊分割成兩塊,一塊是20個字節,一塊是180個字節,然會返回一個指向20個字節的指針。新的180個字節大小的RAM塊將在後續的pvPortMalloc()調用中可用。

Figure 7 演示了 heap_4 最早適應算法如何拼接內存,一樣也演示了內存的分配和釋放:

Figure 7

  1. A演示了建立3個任務以後的數組的樣子,一大塊空的塊存在於數組的頂端。
  2. B演示了刪除1個任務以後的數組,一大塊空的塊存在於數組的頂端。被刪除的那個任務佔據的TCB和棧存儲空間如今是空的,而且它們拼接成一個大的空的塊。
  3. C演示了FreeRTOS建立了一個Queue。隊列是經過xQueueCreate() API 建立的,它是調用pvPortMalloc() 來分配存儲空間的。因爲heap_4採用最早適應算法,pvportMalloc()將會使用第一塊大的足夠容納隊列的RAM塊來分配,在Figure 7中就採用以前刪除任務的那一塊。然而隊列並不徹底消耗那個空閒的區塊,因此那個RAM塊會分紅兩個部分,未使用的部分將會由後續的pvPortMalloc()佔用。
  4. D演示了應用程序直接調用pvPortMalloc()而不是間接地由FreeRTOS API調用以後的情形。用戶分配的區塊足夠小,可以放在第一個空閒的區塊中,這個區塊就是隊列佔用的區塊和後面的TCB佔用的區塊之間的那一塊。
    刪除任務釋放的內存,如今被分割成3個區塊,第一個區塊是隊列,第二個區塊是用戶分配的,第三個區塊仍是空的。
  5. E 演示了隊列刪除以後,存儲空間也自動釋放了。如今用戶分配的區塊兩邊都是空閒區塊。
  6. F 演示了用戶分配的存儲空間釋放的情形。這個區塊如今和兩邊的空閒區塊拼接成了一個更大的空閒區塊。

Heap_4並非肯定性的,可是要比標準庫函數實現的malloc()free()運行的更快。

設定Heap_4數組的起始地址

此章節包含更高階的信息,僅僅爲了使用Heap_4是沒有必要閱讀和理解此章節的。

某些時候應用程序開發者須要指定heap_4數組的起始地址位於某個特定的內存。例如,FreeRTOS 任務的棧是從堆中分配的,就有可能有必要保證堆是分配在快速的內存中,而不是慢速的外存。

默認狀況下,heap_4數組是在heap_4.c源文件中聲明的,它的起始地址是由連接器自動肯定的。然而,若是在文件FreeRTOSConfig.h中把編譯時配置選項configAPPLICATION_ALLOCATED_HEAP設爲常量1,那麼數組必須由使用FreeRTOS的應用聲明。若是把數組聲明爲應用的一部分,那麼應用編寫者能夠指定數組的起始地址。

若是把文件FreeRTOSConfig.h中的configAPPLICATION_ALLOCATED_HEAP設定爲1,那麼應用程序源文件中必須聲明一個名字爲ucHeapuint8_t類型的數組,它的大小有configTOTAL_HEAP_SIZE設定。

把變量放在某個內存地址的語法取決於使用了哪一種編譯器,下面演示了兩種編譯器的用法:

  1. Listing 2演示的是GCC編譯器聲明數組並把數組放在名字爲.my_heap的段中。
  2. Listing 3演示的是IAR編譯器把數組放在內存絕對地址0x20000000上。

uint8_t ucHeap [configTOTAL_HEAP_SIZE] attribute (( section(".my_heap") ));

Listing 2

uint8_t ucHeap [configTOTAL_HEAP_SIZE] @ 0x20000000;

Listing 3

和堆相關的實用函數

xPortGetFreeHeapSize() API

這個函數能夠獲取調用時堆中空閒內存的大小,以字節爲單位。使用它能夠優化堆的大小。例如,當內核對象都建立完畢後調用xPortGetFreeHeapSize()返回2000,那麼能夠把configTOTAL_HEAP_SIZE減少2000.

須要注意,當使用heap_3時是不能調用這個函數的。

xPortGetMinimumEverFreeHeapSize() API

此函數返回FreeRTOS應用程序開始運行以後曾經存在的最小的未被分配的存儲空間的字節數。它的返回值指示了應用程序離將要耗盡堆空間的接近程度。例如xPortGetMinimunEverFreeHeapSize()返回200個字節,那麼從應用程序開始運行以後的某個時間,在使用200個字節就會把堆空間用完。

須要注意,xPortGetMinimumEverFreeHeapSize()只在使用heap_4或者heap_5時生效。

Malloc 失敗鉤子函數

應用程序能夠直接調用pvPortMalloc()。固然在FreeRTOS源文件中每當內核對象建立時也會調用這個函數。此類的內核對象包括任務,隊列,信號量和事件組。

和標準庫函數malloc()同樣,若是pvPortMalloc()由於申請RAM的大小不能知足沒能返回一塊RAM空間就會返回NULL。若是編程人員調用pvPortMalloc()來建立內核對象,可是返回NULL就說明內核對象沒有建立成功。

例子中的全部堆分配方案均可以給pvPortMalloc()配置一個鉤子函數(也稱做回調函數),當pvPortMalloc()返回NULL時調用這個鉤子函數。

若是文件FreeRTOSConfig.h中的configUSE_MALLOC_FAILED_HOOK設置爲1,那麼應用程序必須提供一個內存分配失敗時的鉤子函數,它的名字和原型參見以下。只要對這個應用來講是合適的,這個鉤子函數能夠用任何方法來實現。

void vApplicationMallocFailedHook( void );

聲明

歡迎轉載,請註明出處和做者,同時保留聲明。 做者:LinTeX9527 出處:http://www.cnblogs.com/LinTeX9527/p/8007541.html 本博客的文章如無特殊說明,均爲原創,轉載請註明出處。如未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。

相關文章
相關標籤/搜索