FreeRTOS 筆記_動(dòng)態(tài)內(nèi)存管理
????動(dòng)態(tài)內(nèi)存管理是 FreeRTOS 非常重要的一項(xiàng)功能,任務(wù)創(chuàng)建、信號(hào)量、消息隊(duì)列、事件標(biāo)志組、互斥信號(hào)量、軟件定時(shí)器組等需要的RAM 空間都是通過(guò)動(dòng)態(tài)內(nèi)存管理從 FreeRTOSConfig.h 文件定義的 heap 空間中申請(qǐng)的。
????1?動(dòng)態(tài)內(nèi)存管理介紹
????FreeRTOS 支持 5 種動(dòng)態(tài)內(nèi)存管理方案,分別通過(guò)文件 heap_1,heap_2,heap_3,heap_4 和 heap_5 實(shí)現(xiàn),這 5 個(gè)文件在 FreeRTOS 軟件包中的路徑是:FreeRTOS\Source\portable\MemMang。
????用戶創(chuàng)建的 FreeRTOS 工程項(xiàng)目?jī)H需要 5 種動(dòng)態(tài)內(nèi)存管理方案中的一種。 下面將這 5 種動(dòng)態(tài)內(nèi)存管理方式分別進(jìn)行講解。
1.1 動(dòng)態(tài)內(nèi)存管理方式一 heap_1
????heap_1 動(dòng)態(tài)內(nèi)存管理方式是五種動(dòng)態(tài)內(nèi)存管理方式中最簡(jiǎn)單的,這種方式的動(dòng)態(tài)內(nèi)存管理一旦申請(qǐng) 了相應(yīng)內(nèi)存后,是不允許被釋放的。
????盡管如此,這種方式的動(dòng)態(tài)內(nèi)存管理還是滿足大部分嵌入式應(yīng)用的, 因?yàn)檫@種嵌入式應(yīng)用在系統(tǒng)啟動(dòng)階段就完成了任務(wù)創(chuàng)建、事件標(biāo)志組、信號(hào)量、消息隊(duì)列等資源的創(chuàng)建, 而且這些資源是整個(gè)嵌入式應(yīng)用過(guò)程中一直要使用的,所以也釋放內(nèi)存。
????動(dòng)態(tài)內(nèi)存大小在 FreeRTOSConfig.h 文件中進(jìn)行了定義:?
????#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) //單位字節(jié)
????用戶通過(guò)函數(shù) xPortGetFreeHeapSize 就能獲得 FreeRTOS 動(dòng)態(tài)內(nèi)存的剩余,進(jìn)而可以根據(jù)剩余情況優(yōu)化動(dòng)態(tài)內(nèi)存的大小。
????heap_1 方式的動(dòng)態(tài)內(nèi)存管理有以下特點(diǎn):
????◆ 項(xiàng)目應(yīng)用不需要?jiǎng)h除任務(wù)、信號(hào)量、消息隊(duì)列等已經(jīng)創(chuàng)建的資源。
????◆ 具有時(shí)間確定性,即申請(qǐng)動(dòng)態(tài)內(nèi)存的時(shí)間是固定的并且不會(huì)產(chǎn)生內(nèi)存碎片。
????◆ 靜態(tài)內(nèi)存分配,申請(qǐng)的內(nèi)存是不允許被釋放掉的。
1.2 動(dòng)態(tài)內(nèi)存管理方式二 heap_2
heap_2 動(dòng)態(tài)內(nèi)存管理利用了最適應(yīng)算法,并且支持內(nèi)存釋放。?
但是 heap_2 不支持內(nèi)存碎片整理,動(dòng)態(tài)內(nèi)存僅管理方式四 heap_4 支持內(nèi)存碎片整理。
????用戶通過(guò)函數(shù) xPortGetFreeHeapSize 就能獲得 FreeRTOS 動(dòng)態(tài)內(nèi)存的剩余,用戶可以根據(jù)剩余情況優(yōu)化動(dòng)態(tài)內(nèi)存的大小,但是不提供動(dòng)態(tài)內(nèi)存是如何被分配成各個(gè)小內(nèi)存塊的信息。
????動(dòng)態(tài)內(nèi)存大小在 FreeRTOSConfig.h 文件中進(jìn)行了定義:?
?????#define configTOTAL_HEAP_SIZE?( ( size_t ) ( 17 * 1024 ) ) //單位字節(jié)
????heap_2 方式的動(dòng)態(tài)內(nèi)存管理有以下特點(diǎn):
????◆ 不考慮內(nèi)存碎片的情況下,這種方式支持重復(fù)的任務(wù)、信號(hào)量、事件標(biāo)志組、軟件定時(shí)器等內(nèi)部資源的創(chuàng)建和刪除。
????◆ 如果用戶申請(qǐng)和釋放的動(dòng)態(tài)內(nèi)存大小是隨機(jī)的,這樣容易產(chǎn)生內(nèi)存碎片,內(nèi)存碎片過(guò)多的話會(huì)導(dǎo)致無(wú)法申請(qǐng)出一個(gè)大的內(nèi)存塊出來(lái),不建議采用這種動(dòng)態(tài)內(nèi)存管理方式。
????◆ 如果用戶隨機(jī)的創(chuàng)建和刪除任務(wù)、消息隊(duì)列、事件標(biāo)志組、信號(hào)量等內(nèi)部資源也容易出現(xiàn)內(nèi)存碎片。
????◆ heap_2 方式實(shí)現(xiàn)的動(dòng)態(tài)內(nèi)存申請(qǐng)不具有時(shí)間確定性,但是比 C 庫(kù)中的 malloc 函數(shù)效率要高。
????大部分需要?jiǎng)討B(tài)內(nèi)存申請(qǐng)和釋放的小型實(shí)時(shí)系統(tǒng)項(xiàng)目可以使用 heap_2。如果需要內(nèi)存碎片的回收機(jī) 制可以使用 heap_4。
1.3 動(dòng)態(tài)內(nèi)存管理方式三 heap_3
heap_3實(shí)現(xiàn)的動(dòng)態(tài)內(nèi)存管理是對(duì)malloc 和 free 函數(shù)進(jìn)行了封裝,保證是線程安全的。
????heap_3 方式的動(dòng)態(tài)內(nèi)存管理有以下特點(diǎn):
???? ◆ 需要編譯器提供 malloc 和 free 函數(shù)。?
????◆ 不具有時(shí)間確定性,即申請(qǐng)動(dòng)態(tài)內(nèi)存的時(shí)間不是固定的。
???? ◆ 增加 RTOS 內(nèi)核的代碼量。
另外要特別注意一點(diǎn),這種方式的動(dòng)態(tài)內(nèi)存申請(qǐng)和釋放不是用的 FreeRTOSConfig.h 文件中定義的heap空間大小,而是用的編譯器設(shè)置的heap空間大小或者說(shuō)STM32啟動(dòng)代碼中設(shè)置的heap空間大小, 比如 MDK 版本的 STM32F103 工程中 heap 大小就是在這里進(jìn)行的定義:

1.4 動(dòng)態(tài)內(nèi)存管理方式四 heap_4
heap_4 動(dòng)態(tài)內(nèi)存管理利用了最適應(yīng)算法,且支持內(nèi)存碎片的回 收并將其整理為一個(gè)大的內(nèi)存塊。
????FreeRTOS 的動(dòng)態(tài)內(nèi)存大小在 FreeRTOSConfig.h 文件中進(jìn)行了定義:
????#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) //單位字節(jié)
heap_4 同時(shí)支持將動(dòng)態(tài)內(nèi)存設(shè)置在指定的 RAM 空間位置。
????用戶通過(guò)函數(shù) xPortGetFreeHeapSize 就能獲得 FreeRTOS 動(dòng)態(tài)內(nèi)存的剩余,但是不提供動(dòng)態(tài)內(nèi)存是 如何被分配成各個(gè)小內(nèi)存塊的信息。
????使用函數(shù) xPortGetMinimumEverFreeHeapSize 能夠獲取從系統(tǒng)啟 動(dòng)到當(dāng)前時(shí)刻的動(dòng)態(tài)內(nèi)存最小剩余,從而用戶就可以根據(jù)剩余情況優(yōu)化動(dòng)態(tài)內(nèi)存的大小。
????heap_4 方式的 動(dòng)態(tài)內(nèi)存管理有以下特點(diǎn):
◆ 可以用于需要重復(fù)的創(chuàng)建和刪任務(wù)、信號(hào)量、事件標(biāo)志組、軟件定時(shí)器等內(nèi)部資源的場(chǎng)合。
◆ 隨機(jī)的調(diào)用 pvPortMalloc() 和 vPortFree(),且每次申請(qǐng)的大小都不同,也不會(huì)像 heap_2 那樣產(chǎn) 生很多的內(nèi)存碎片。?
◆ 不具有時(shí)間確定性,即申請(qǐng)動(dòng)態(tài)內(nèi)存的時(shí)間不是確定的,但是比 C 庫(kù)中的 malloc 函數(shù)要高效。 heap_4 比較實(shí)用,用戶的代碼也可以直接調(diào)用函數(shù) pvPortMalloc() 和 vPortFree()進(jìn)行動(dòng)態(tài)內(nèi)存的申請(qǐng)和釋放。
1.5 動(dòng)態(tài)內(nèi)存管理方式五 heap_5
????有時(shí)候我們希望 FreeRTOSConfig.h 文件中定義的 heap 空間可以采用不連續(xù)的內(nèi)存區(qū),比如我們希望可以將其定義在內(nèi)部 SRAM 一部分,外部 SRAM 一部分,此時(shí)我們就可以采用 heap_5 動(dòng)態(tài)內(nèi)存管理方式。另外,heap_5 動(dòng)態(tài)內(nèi)存管理是在 heap_4 的基礎(chǔ)上實(shí)現(xiàn)的。
????heap_5 動(dòng)態(tài)內(nèi)存管理是通過(guò)函數(shù) vPortDefineHeapRegions 進(jìn)行初始化的,也就是說(shuō)用戶在創(chuàng)建任務(wù) FreeRTOS 的內(nèi)部資源前要優(yōu)先級(jí)調(diào)用這個(gè)函數(shù) vPortDefineHeapRegions,否則是無(wú)法通過(guò)函數(shù) pvPortMalloc 申請(qǐng)到動(dòng)態(tài)內(nèi)存的。?
????函數(shù) vPortDefineHeapRegions 定義不同段的內(nèi)存空間采用了下面這種結(jié)構(gòu)體:
比如下面定義了兩個(gè)內(nèi)存塊:
定義的時(shí)候要注意兩個(gè)問(wèn)題,一個(gè)是內(nèi)存段結(jié)束時(shí)要定義 NULL。另一個(gè)是內(nèi)存段的地址是從低地址到高地址排列。
????用戶通過(guò)函數(shù) xPortGetFreeHeapSize 就能獲得 FreeRTOS 動(dòng)態(tài)內(nèi)存的剩余,但是不提供動(dòng)態(tài)內(nèi)存是 如何被分配成各個(gè)小內(nèi)存塊的信息。使用函數(shù) xPortGetMinimumEverFreeHeapSize 能夠獲取從系統(tǒng)啟動(dòng)到當(dāng)前時(shí)刻的動(dòng)態(tài)內(nèi)存最小剩余,從而用戶就可以根據(jù)剩余情況優(yōu)化動(dòng)態(tài)內(nèi)存的大小。
1.6 五種動(dòng)態(tài)內(nèi)存方式總結(jié)
五種動(dòng)態(tài)內(nèi)存管理方式簡(jiǎn)單總結(jié)如下,實(shí)際項(xiàng)目中,用戶根據(jù)需要選擇合適的:
◆ heap_1:五種方式里面最簡(jiǎn)單的,但是申請(qǐng)的內(nèi)存不允許釋放。
◆ heap_2:支持動(dòng)態(tài)內(nèi)存的申請(qǐng)和釋放,但是不支持內(nèi)存碎片的處理,并將其合并成一個(gè)大的內(nèi)存塊。?
◆ heap_3:將編譯器自帶的 malloc 和 free 函數(shù)進(jìn)行簡(jiǎn)單的封裝,以支持線程安全,即支持多任務(wù)調(diào)用。?
◆ heap_4:支持動(dòng)態(tài)內(nèi)存的申請(qǐng)和釋放,支持內(nèi)存碎片處理,支持將動(dòng)態(tài)內(nèi)存設(shè)置在個(gè)固定的地址。?
◆ heap_5:在 heap_4 的基礎(chǔ)上支持將動(dòng)態(tài)內(nèi)存設(shè)置在不連續(xù)的區(qū)域上。