1. 程式人生 > >FreeRTOS 動態內存管理

FreeRTOS 動態內存管理

嵌入 動態 獲得 最小 相同 -s 無法 ati 內存碎片

本章節為大家講解 FreeRTOS 動態內存管理,動態內存管理是 FreeRTOS 非常重要的一項功能,前面
章節講解的任務創建、 信號量、 消息隊列、 事件標誌組、 互斥信號量、 軟件定時器組等需要的 RAM 空間
都是通過動態內存管理從 FreeRTOSConfig.h 文件定義的 heap 空間中申請的。
動態內存管理介紹
FreeRTOS 支持 5 種動態內存管理方案,分別通過文件 heap_1,heap_2,heap_3,heap_4 和 heap_5
實現,這 5 個文件在 FreeRTOS 軟件包中的路徑是:FreeRTOS\Source\portable\MemMang。 用戶創
建的 FreeRTOS 工程項目僅需要 5 種方式中的一種。
下面將這 5 種動態內存管理方式分別進行講解。

動態內存管理方式一 heap_1
heap_1 動態內存管理方式是五種動態內存管理方式中最簡單的,這種方式的動態內存管理一旦申請
了相應內存後,是不允許被釋放的。 盡管如此,這種方式的動態內存管理還是滿足大部分嵌入式應用的,
因為這種嵌入式應用在系統啟動階段就完成了任務創建、 事件標誌組、 信號量、 消息隊列等資源的創建,
而且這些資源是整個嵌入式應用過程中一直要使用的,所以也就不需要刪除,即釋放內存。 FreeRTOS 的
動態內存大小在 FreeRTOSConfig.h 文件中進行了定義:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) //單位字節
用戶通過函數 xPortGetFreeHeapSize 就能獲得 FreeRTOS 動態內存的剩余,進而可以根據剩余情況優化
動態內存的大小。 heap_1 方式的動態內存管理有以下特點:
? 項目應用不需要刪除任務、 信號量、 消息隊列等已經創建的資源。
? 具有時間確定性,即申請動態內存的時間是固定的並且不會產生內存碎片。

? 確切的說這是一種靜態內存分配,因為申請的內存是不允許被釋放掉的。
動態內存管理方式二 heap_2
與 heap_1 動態內存管理方式不同,heap_2 動態內存管理利用了最適應算法,並且支持內存釋放。
但是 heap_2 不支持內存碎片整理,動態內存管理方式四 heap_4 支持內存碎片整理。 FreeRTOS 的動態
內存大小在 FreeRTOSConfig.h 文件中進行了定義:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) //單位字節
用戶通過函數 xPortGetFreeHeapSize 就能獲得 FreeRTOS 動態內存的剩余,但是不提供動態內存是
如何被分配成各個小內存塊的信息。 另外,就是用戶可以根據剩余情況優化動態內存的大小。 heap_2 方
式的動態內存管理有以下特點:
? 不考慮內存碎片的情況下,這種方式支持重復的任務、 信號量、 事件標誌組、 軟件定時器等內部資源
的創建和刪除。
? 如果用戶申請和釋放的動態內存大小是隨機的,不建議采用這種動態內存管理方式,比如:
? 項目應用中需要重復的創建和刪除任務,如果每次創建需要動態內存大小相同,那麽 heap_2 比
較適合,但每次創建需要動態內存大小不同,那麽方式 heap_2 就不合適了,因為容易產生內存
碎片,內存碎片過多的話會導致無法申請出一個大的內存塊出來,這種情況使用 heap_4 比較合
適。
? 項目應用中需要重復的創建和刪除消息隊列,也會出現類似上面的情況,這種情況下使用 heap_4
比較合適。
? 直接的調用函數 pvPortMalloc() 和 vPortFree()也容易出現內存碎片。 如果用戶按一定順序成
對的申請和釋放,基本沒有內存碎片的,而不按順序的隨機申請和釋放容易產生內存碎片。
? 如果用戶隨機的創建和刪除任務、 消息隊列、 事件標誌組、 信號量等內部資源也容易出現內存碎片。
? heap_2 方式實現的動態內存申請不具有時間確定性,但是比 C 庫中的 malloc 函數效率要高。
大部分需要動態內存申請和釋放的小型實時系統項目可以使用 heap_2。 如果需要內存碎片的回收機
制可以使用 heap_4。
動態內存管理方式三 heap_3
這種方式實現的動態內存管理是對編譯器提供的 malloc 和 free 函數進行了封裝,保證是線程安全的。
heap_3 方式的動態內存管理有以下特點:
? 需要編譯器提供 malloc 和 free 函數。
? 不具有時間確定性,即申請動態內存的時間不是固定的。
? 增加 RTOS 內核的代碼量。
另外要特別註意一點,這種方式的動態內存申請和釋放不是用的 FreeRTOSConfig.h 文件中定義的
heap空間大小,而是用的編譯器設置的heap空間大小或者說STM32啟動代碼中設置的heap空間大小,
比如 MDK 版本的 STM32F103 工程中 heap 大小就是在這裏進行的定義:
技術分享

動態內存管理方式四 heap_4
與 heap_2 動態內存管理方式不同,heap_4 動態內存管理利用了最適應算法,且支持內存碎片的回
收並將其整理為一個大的內存塊。 FreeRTOS 的動態內存大小在 FreeRTOSConfig.h 文件中進行了定義:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) //單位字節
heap_4 同時支持將動態內存設置在指定的 RAM 空間位置。
用戶通過函數 xPortGetFreeHeapSize 就能獲得 FreeRTOS 動態內存的剩余,但是不提供動態內存是
如何被分配成各個小內存塊的信息。 使用函數 xPortGetMinimumEverFreeHeapSize 能夠獲取從系統啟
動到當前時刻的動態內存最小剩余,從而用戶就可以根據剩余情況優化動態內存的大小。 heap_4 方式的
動態內存管理有以下特點:
? 可以用於需要重復的創建和刪任務、 信號量、 事件標誌組、 軟件定時器等內部資源的場合。
? 隨機的調用 pvPortMalloc() 和 vPortFree(),且每次申請的大小都不同,也不會像 heap_2 那樣產
生很多的內存碎片。
? 不具有時間確定性,即申請動態內存的時間不是確定的,但是比 C 庫中的 malloc 函數要高效。
heap_4 比較實用,本教程配套的所有例子都是用的這種方式的動態內存管理,用戶的代碼也可以直
接調用函數 pvPortMalloc() 和 vPortFree()進行動態內存的申請和釋放。
動態內存管理方式五 heap_5
有時候我們希望 FreeRTOSConfig.h 文件中定義的 heap 空間可以采用不連續的內存區,比如我們希
望可以將其定義在內部 SRAM 一部分,外部 SRAM 一部分,此時我們就可以采用 heap_5 動態內存管理
方式。另外,heap_5 動態內存管理是在 heap_4 的基礎上實現的。
heap_5 動態內存管理是通過函數 vPortDefineHeapRegions 進行初始化的,也就是說用戶在創建任
務 FreeRTOS 的內部資源前要優先級調用這個函數 vPortDefineHeapRegions,否則是無法通過函數
pvPortMalloc 申請到動態內存的。
函數 vPortDefineHeapRegions 定義不同段的內存空間采用了下面這種結構體:
技術分享

定義的時候要註意兩個問題,一個是內存段結束時要定義 NULL。另一個是內存段的地址是從低地址到高
地址排列。
用戶通過函數 xPortGetFreeHeapSize 就能獲得 FreeRTOS 動態內存的剩余,但是不提供動態內存是
如何被分配成各個小內存塊的信息。 使用函數 xPortGetMinimumEverFreeHeapSize 能夠獲取從系統啟
動到當前時刻的動態內存最小剩余,從而用戶就可以根據剩余情況優化動態內存的大小。

五種動態內存方式總結
五種動態內存管理方式簡單總結如下,實際項目中,用戶根據需要選擇合適的:
? heap_1:五種方式裏面最簡單的,但是申請的內存不允許釋放。
? heap_2:支持動態內存的申請和釋放,但是不支持內存碎片的處理,並將其合並成一個大的內存塊。
? heap_3:將編譯器自帶的 malloc 和 free 函數進行簡單的封裝,以支持線程安全,即支持多任務調
用。
? heap_4:支持動態內存的申請和釋放,支持內存碎片處理,支持將動態內存設置在個固定的地址。
? heap_5:在 heap_4 的基礎上支持將動態內存設置在不連續的區域上。

動態內存和靜態內存比較
靜態內存方式是從 FreeRTOS 的 V9.0.0 版本才開始有的,而我們本次教程使用的版本是 V8.2.3。所
以靜態內存方式我們暫時不做講解,等 FreeRTOS 教程版本升級時再做講解。 關於靜態內存方式和動態內
存方式的優缺點可以看官方的此貼說明:點擊查看
(制作此教程的時候,官方的 FreeRTOS V9.0.0 正式版本還沒有發布,所以采用的是當前最新的 V8.2.3)
動態內存 API 函數
動態內存的 API 函數在官方的在線版手冊上面沒有列出,其實使用也比較簡單,類似 C 庫的 malloc
和 free 函數,具體使用參看下面的實例說明。
實驗練兵場:

FreeRTOS 動態內存管理