FreeRTOS的內(nèi)存管理和堆的問(wèn)題,。從 V9.0.0 開(kāi)始,F(xiàn)reeRTOS 應(yīng)用程序可以完全靜態(tài)分配,,這意味著無(wú)需包含堆內(nèi)存管理器。 https://www./a00111.html 嵌入式專欄 1 動(dòng)態(tài)內(nèi)存分配與FreeRTOS的關(guān)聯(lián)性: 要讓 FreeRTOS 對(duì)象(如任務(wù),、隊(duì)列、信號(hào)量和事件組)變得盡可能易于使用,,這些內(nèi)核對(duì)象不是在編譯時(shí)靜態(tài)分配,,而是在運(yùn)行時(shí)動(dòng)態(tài)分配。每次創(chuàng)建內(nèi)核對(duì)象時(shí),,F(xiàn)reeRTOS 都會(huì)分配 RAM,; 嵌入式專欄 2 嵌入式專欄 3 heap_1:最簡(jiǎn)單,,不允許釋放內(nèi)存,。 heap_2:允許釋放內(nèi)存,但不能合并相鄰的空閑塊,。 heap_3:簡(jiǎn)單包裝標(biāo)準(zhǔn)的malloc()和free()以確保線程安全,。 heap_4:合并相鄰的空閑塊以避免碎片。包括絕對(duì)地址放置選項(xiàng),。 heap_5:按照heap_4,,具有跨多個(gè)不相鄰的內(nèi)存區(qū)域擴(kuò)展堆的能力。 Heap_1 它常用于小型專用嵌入式系統(tǒng),,以便僅在啟動(dòng)計(jì)劃程序之前創(chuàng)建任務(wù)和其他內(nèi)核對(duì)象,。在應(yīng)用程序開(kāi)始執(zhí)行任何實(shí)時(shí)功能之前,內(nèi)核動(dòng)態(tài)分配內(nèi)存,,并且內(nèi)存在應(yīng)用程序的生命周期內(nèi)保持已分配狀態(tài),。這意味著所選分配方案不必考慮任何更復(fù)雜的內(nèi)存分配問(wèn)題(例如確定性和碎片化),而是可以考慮如代碼大小和簡(jiǎn)單性等屬性,。 Heap_1.c 實(shí)現(xiàn) pvPortMalloc() 的一個(gè)非常基本的版本,。它不實(shí)現(xiàn) vPortFree(),。從不刪除任務(wù)或其他內(nèi)核對(duì)象的應(yīng)用程序可以使用 heap_1。 某些商業(yè)關(guān)鍵和安全關(guān)鍵型系統(tǒng)可能禁止動(dòng)態(tài)內(nèi)存分配,,這些系統(tǒng)也可能能夠使用 heap_1,。由于存在與非確定性、內(nèi)存碎片化以及分配失敗等相關(guān)的不確定性,,因此,,關(guān)鍵系統(tǒng)通常禁止動(dòng)態(tài)內(nèi)存分配,但 heap_1 始終是確定性的且無(wú)法對(duì)內(nèi)存進(jìn)行碎片化,。 當(dāng)調(diào)用 pvPortMalloc() 時(shí),,heap_1 分配方案將一個(gè)簡(jiǎn)單的數(shù)組細(xì)分成更小的塊。此數(shù)組稱為 FreeRTOS堆,。 數(shù)組的總大?。ㄒ宰止?jié)為單位)由定義 configTOTAL_HEAP_SIZE 在 FreeRTOSConfig.h 中設(shè)置。以這種方式定義大型數(shù)組可能讓應(yīng)用程序看起來(lái)會(huì)消耗大量 RAM,,甚至從數(shù)組中分配任何內(nèi)存之前就是如此,。 每個(gè)創(chuàng)建的任務(wù)都要求從堆中分配一個(gè)任務(wù)控制塊 (TCB) 和一個(gè)堆棧。 下圖顯示了在創(chuàng)建任務(wù)時(shí) heap_1 如何細(xì)分簡(jiǎn)單的數(shù)組。每次創(chuàng)建任務(wù)時(shí),,都會(huì)從 heap_1 數(shù)組分配 RAM,。 A 顯示創(chuàng)建任何任務(wù)之前的數(shù)組。整個(gè)數(shù)組都可用,。 B 顯示已創(chuàng)建一個(gè)任務(wù)后的數(shù)組,。 C 顯示已創(chuàng)建三個(gè)任務(wù)后的數(shù)組。 Heap_2 Heap_2 包含在 FreeRTOS 發(fā)行版中以保持向后兼容性,。建議不要用于新設(shè)計(jì),,而是考慮使用 heap_4,因?yàn)槠渲刑峁┝烁喙δ堋?/span> 也可以使用 Heap_2.c,,但要細(xì)分由 configTOTAL_HEAP_SIZE 確定大小的數(shù)組,。它使用最適合算法分配內(nèi)存。與 heap_1 不同,,它允許釋放內(nèi)存,。再次說(shuō)明,數(shù)組是靜態(tài)聲明的,,因此應(yīng)用程序看起來(lái)會(huì)消耗大量RAM,,甚至在從數(shù)組中分配任何內(nèi)存之前就是如此。 最適合算法可確保 pvPortMalloc() 使用的可用內(nèi)存塊在大小方面與所要求的字節(jié)數(shù)最接近,。例如,,考慮以下情形: 適合所請(qǐng)求字節(jié)數(shù)的最小可用 RAM 塊是 25 字節(jié)塊,,因此,,pvPortMalloc() 將 25 字節(jié)塊拆分成一個(gè) 20 字節(jié)塊和一個(gè) 5 字節(jié)塊,然后返回一個(gè)指向 20 字節(jié)塊的指針,。(上面是過(guò)于簡(jiǎn)化了,,因?yàn)?heap_2 要存儲(chǔ)堆區(qū)域中塊大小的信息,因此兩個(gè)拆分塊的總和實(shí)際上將小于 25,。) 新的 5 字節(jié)塊保持可用于將來(lái)對(duì)pvPortMalloc() 的調(diào)用,。 與 heap_4 不同,heap_2 不將相鄰的可用塊合并為單個(gè)更大的塊,。因此,,它更容易碎片化。但是,,如果分配的塊與后續(xù)釋放的塊大小始終相同,,則碎片化不是問(wèn)題,。Heap_2 適合反復(fù)創(chuàng)建和刪除任務(wù)的應(yīng)用程序,但前提是分配給所創(chuàng)建的任務(wù)的堆棧大小不發(fā)生變化,。 下圖顯示在創(chuàng)建和刪除任務(wù)時(shí)從 heap_2 數(shù)組中分配和釋放 RAM 的過(guò)程,。 圖中顯示當(dāng)創(chuàng)建、刪除以及后續(xù)再次創(chuàng)建任務(wù)時(shí),,最適合算法的工作原理,。 · A 顯示已創(chuàng)建三個(gè)任務(wù)后的數(shù)組。大型可用塊保持在數(shù)組的頂部,。 · B 顯示已刪除其中一個(gè)任務(wù)后的數(shù)組,。這些區(qū)域有: 數(shù)組頂部的大型可用塊保持原樣。此外,,目前有兩個(gè)較小的可用塊,,它們之前分配給了已刪除任務(wù)的TCB 和堆棧。 · C 顯示已創(chuàng)建另一個(gè)任務(wù)后的數(shù)組,。創(chuàng)建任務(wù)導(dǎo)致兩次調(diào)用 pvPortMalloc():一次是分配新的 TCB,,一次是分配任務(wù)堆棧。使用 xTaskCreate() API 函數(shù)創(chuàng)建任務(wù),,如創(chuàng)建任務(wù) (p. 23)中所述,。在 xTaskCreate() 內(nèi)部發(fā)生兩次 pvPortMalloc() 調(diào)用。 每個(gè) TCB 的大小完全相同,,因此,,最適合算法可確保之前分配給已刪除任務(wù)的 TCB 的 RAM 塊可重用于分配新任務(wù)的 TCB。 分配給新創(chuàng)建任務(wù)的堆棧大小與分配給以前被刪除任務(wù)的堆棧大小完全相同,,因此,,最適合算法可確保之前分配給已刪除任務(wù)的堆棧的 RAM 塊可重用于分配新任務(wù)的堆棧。 數(shù)組頂部的較大的未分配塊保持不變,。Heap_2 是非確定性的,但它比 malloc() 和 free() 的大多數(shù)標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)都要快,。 Heap_3 Heap_3.c 使用標(biāo)準(zhǔn)庫(kù)函數(shù) malloc() 和 free(),,因此堆大小由鏈接器配置定義。configTOTAL_HEAP_SIZE 設(shè)置不起作用,。 Heap_3 通過(guò)臨時(shí)暫停 FreeRTOS 計(jì)劃程序使 malloc() 和 free() 成為線程安全的,。 Heap_4 與 heap_1 和 heap_2 一樣,heap_4 將數(shù)組細(xì)分成較小的塊,。數(shù)組是靜態(tài)聲明的并由configTOTAL_HEAP_SIZE 確定大小,,因此應(yīng)用程序看起來(lái)會(huì)消耗大量 RAM,甚至在從數(shù)組中分配任何內(nèi)存之前就是如此,。 Heap_4 使用首個(gè)適合算法分配內(nèi)存,。與 heap_2 不同,,它會(huì)將相鄰的可用內(nèi)存塊組合(合并)成一個(gè)較大的塊。這樣可以最大程度地減小內(nèi)存碎片化風(fēng)險(xiǎn),。 首個(gè)適合算法可確保 pvPortMalloc() 使用第一個(gè)大小足以容納所要求的字節(jié)數(shù)的可用內(nèi)存塊,。例如,考慮以下情形: 適合所請(qǐng)求字節(jié)數(shù)的第一個(gè)可用 RAM 塊是 200 字節(jié)塊,,因此,pvPortMalloc() 將 200 字節(jié)塊拆分成一個(gè) 20字節(jié)塊和一個(gè) 180 字節(jié)塊,,然后返回一個(gè)指向 20 字節(jié)塊的指針,。(上面是過(guò)于簡(jiǎn)化了,因?yàn)?heap_4 要存儲(chǔ)堆區(qū)域中塊大小的信息,,因此兩個(gè)拆分塊的總和將小于 200 字節(jié),。) 新的 180 字節(jié)塊保持可用于將來(lái)對(duì)pvPortMalloc() 的調(diào)用。 Heap_4 會(huì)將相鄰的可用內(nèi)存塊組合(合并)成一個(gè)較大的塊,,同時(shí)最大限度地降低碎片化風(fēng)險(xiǎn),。Heap_4 適用于反復(fù)分配和釋放不同大小的 RAM 塊的應(yīng)用程序。 下圖顯示從 heap_4 數(shù)組中分配和釋放 RAM 的過(guò)程,。它演示 heap_4 首個(gè)適合算法(帶內(nèi)存合并)在分配和釋放內(nèi)存時(shí)的工作方式,。 A 顯示已創(chuàng)建三個(gè)任務(wù)后的數(shù)組。大型可用塊保持在數(shù)組頂部,。 B 顯示已刪除其中一個(gè)任務(wù)后的數(shù)組,。 C 顯示已創(chuàng)建一個(gè) FreeRTOS 隊(duì)列后的數(shù)組。 D 顯示在從應(yīng)用程序代碼中直接調(diào)用 pvPortMalloc()(而不是通過(guò)間接調(diào)用 FreeRTOS API 函數(shù))后的數(shù)組,。 E 顯示刪除隊(duì)列后的數(shù)組,,此時(shí)會(huì)自動(dòng)釋放分配給已刪除隊(duì)列的內(nèi)存。此時(shí),,用戶分配的塊的兩側(cè)都有可用內(nèi)存,。 F 顯示的也是已釋放用戶分配的內(nèi)存之后的數(shù)組。用戶分配的塊所用的內(nèi)存已與兩側(cè)的可用內(nèi)存組合成一個(gè)更大的可用塊,。 Heap_4 是非確定性的,,但比 malloc() 和 free() 的大多數(shù)標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)都要快。 Heap_5 heap_5 用來(lái)分配和釋放內(nèi)存的算法與 heap_4 的完全相同,。與 heap_4 不同,,heap_5 不限于從單個(gè)靜態(tài)聲明的數(shù)組分配內(nèi)存。Heap_5 可以從多個(gè)單獨(dú)的內(nèi)存空間分配內(nèi)存,。當(dāng)運(yùn)行 FreeRTOS 的系統(tǒng)提供的 RAM在系統(tǒng)的內(nèi)存映射中未作為單個(gè)鄰接的(沒(méi)有空間)塊出現(xiàn)時(shí),,Heap_5 很有用,。 Heap_5 是唯一一個(gè)必須在調(diào)用 pvPortMalloc() 之前顯式初始化的內(nèi)存分配方案。它使用 vPortDefineHeapRegions() API 函數(shù)進(jìn)行初始化,。當(dāng)使用 heap_5 時(shí),,必須先調(diào)用vPortDefineHeapRegions(),然后才可創(chuàng)建任何內(nèi)核對(duì)象(任務(wù),、隊(duì)列,、信號(hào)等)。 ------------ END ------------ |
|