FreeRTOS 教程指南 学习笔记 第二章 内存管理
FreeRTOS 教程指南 學(xué)習(xí)筆記 第二章 內(nèi)存管理
一、簡(jiǎn)介
本書的以下章節(jié)將介紹內(nèi)核對(duì)象,如任務(wù)、隊(duì)列、信號(hào)量和事件組。為了使FreeRTOS盡可能容易使用,這些內(nèi)核對(duì)象不是在編譯時(shí)靜態(tài)分配,而是在運(yùn)行時(shí)動(dòng)態(tài)分配;FreeRTOS在每次創(chuàng)建內(nèi)核對(duì)象時(shí)分配RAM,并在每次刪除內(nèi)核對(duì)象時(shí)釋放RAM。此策略減少了設(shè)計(jì)和規(guī)劃工作,簡(jiǎn)化了API,并最小化了RAM占用。
本章討論了動(dòng)態(tài)內(nèi)存分配。動(dòng)態(tài)內(nèi)存分配是一個(gè)C語(yǔ)言編程的概念,而不是一個(gè)特定于FreeRTOS或多任務(wù)處理的概念。它與FreeRTOS相關(guān),因?yàn)閮?nèi)核對(duì)象是動(dòng)態(tài)分配的,而且由通用編譯器提供的動(dòng)態(tài)內(nèi)存分配方案并不總是適合于實(shí)時(shí)應(yīng)用程序。
二、動(dòng)態(tài)內(nèi)存分配及其與FreeRTOS的相關(guān)性
內(nèi)存可以使用標(biāo)準(zhǔn)的C庫(kù)malloc()和免費(fèi)的()函數(shù)進(jìn)行分配,但它們可能不適合,或不適合,因?yàn)橐韵乱粋€(gè)或多個(gè)原因:
- 它們并不總是在小型嵌入式系統(tǒng)上可用。
- 它們的實(shí)現(xiàn)可以相對(duì)較大,占用有價(jià)值的代碼空間。
- 它們很少是線程安全的。
- 它們不是確定性的;執(zhí)行函數(shù)所花費(fèi)的時(shí)間與調(diào)用不同。
- 他們可能會(huì)是碎片化的。
- 它們可以使連接器的配置復(fù)雜化。
- 如果允許堆空間增長(zhǎng)到其他變量使用的內(nèi)存中,它們可能成為難以調(diào)試錯(cuò)誤的來(lái)源
三、用于動(dòng)態(tài)內(nèi)存分配的選項(xiàng)
FreeRTOS現(xiàn)在將內(nèi)存分配視為可移植層的一部分(而不是核心代碼庫(kù)的一部分)。這是為了認(rèn)識(shí)到不同的嵌入式系統(tǒng)具有不同的動(dòng)態(tài)內(nèi)存分配和時(shí)間要求,因此一個(gè)單一的動(dòng)態(tài)內(nèi)存分配算法將永遠(yuǎn)只適用于應(yīng)用程序的一個(gè)子集。此外,從核心代碼庫(kù)中刪除動(dòng)態(tài)內(nèi)存分配可以使應(yīng)用程序編寫器能夠在適當(dāng)?shù)臅r(shí)候提供它們自己的特定實(shí)現(xiàn)。
當(dāng)FreeRTOS需要RAM時(shí),它不是調(diào)用Malloc(),而是調(diào)用pvPortMalloc()。當(dāng)釋放RAM時(shí),內(nèi)核不是調(diào)用free(),而是調(diào)用vPortFree()。pvPortMalloc()與標(biāo)準(zhǔn)C庫(kù)malloc()函數(shù)具有相同的原型,而vPortFree()與標(biāo)準(zhǔn)C庫(kù)free()函數(shù)具有相同的原型。
pvPortMalloc()和vPortFree()是公共函數(shù),因此也可以從應(yīng)用程序代碼中調(diào)用。
FreeRTOS提供了pvPortMalloc()和vPortFree()的五個(gè)實(shí)現(xiàn)示例,所有這些都將在本章中記錄。FreeRTOS應(yīng)用程序可以使用其中一個(gè)示例實(shí)現(xiàn),或者提供它們自己的實(shí)現(xiàn)。這五個(gè)例子分別在heap_1.c、heap_2.c、heap_3.c、heap_4.c和heap_5.c源文件中進(jìn)行了定義,所有這些文件都位于FreeRTOS/Source/portable/MemMang目錄中。
本章旨在讓讀者能夠很好地理解:
- FreeRTOS何時(shí)進(jìn)行RAM分配。
- 介紹FreeRTOS提供的五個(gè)內(nèi)存分配方案示例。
- 如何選擇內(nèi)存分配方案。
四、內(nèi)存分配方案的示例
Heap_1
對(duì)于小型專用嵌入式系統(tǒng),通常在調(diào)度程序啟動(dòng)之前只創(chuàng)建任務(wù)和其他內(nèi)核對(duì)象。在這種情況下,只有在應(yīng)用程序開(kāi)始執(zhí)行任何實(shí)時(shí)功能之前,內(nèi)核才能動(dòng)態(tài)地分配內(nèi)存,并且內(nèi)存會(huì)保持分配到應(yīng)用程序的整個(gè)生命周期。這意味著所選擇的分配方案不必考慮任何更復(fù)雜的內(nèi)存分配問(wèn)題,如決定論和碎片化,而可以只考慮代碼大小和簡(jiǎn)單性等屬性。
Heap_1.c實(shí)現(xiàn)了一個(gè)非常基本的pvPortMalloc()版本,而沒(méi)有實(shí)現(xiàn)vPortFree()。從未刪除任務(wù)或其他內(nèi)核對(duì)象的應(yīng)用程序有可能使用heap_1方式。一些原本會(huì)禁止使用動(dòng)態(tài)內(nèi)存分配的商業(yè)關(guān)鍵和安全關(guān)鍵系統(tǒng)也有可能使用heap_1。關(guān)鍵系統(tǒng)通常禁止動(dòng)態(tài)內(nèi)存分配,因?yàn)榇嬖趦?nèi)存碎片和失敗的分配相關(guān)的不確定性——但Heap_1總是確定性的,不能將內(nèi)存碎片分割。
heap_1分配方案將一個(gè)簡(jiǎn)單的數(shù)組細(xì)分為更小的塊,作為對(duì)pvPortMalloc()的調(diào)用。該數(shù)組被稱為FreeRTOS堆。數(shù)組的總大小(以字節(jié)為單位)由FreeRTOSConfig.h中的定義configTOTAL_HEAP_SIZE設(shè)置。以這種方式定義一個(gè)大數(shù)組會(huì)使應(yīng)用程序消耗大量RAM——甚至在從數(shù)組分配任何內(nèi)存之前。每個(gè)已創(chuàng)建的任務(wù)都需要從堆中分配一個(gè)任務(wù)控制塊(TCB)和一個(gè)堆棧。圖5演示了heap_1如何在創(chuàng)建任務(wù)時(shí)細(xì)分簡(jiǎn)單數(shù)組。
- 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í)現(xiàn)向后兼容,但不建議在新設(shè)計(jì)中使用它。請(qǐng)考慮使用heap_4而不是heap_2,因?yàn)閔eap_4提供了增強(qiáng)的功能。Heap_2.c同樣通過(guò)細(xì)分一個(gè)由configTOTAL_HEAP_SIZE標(biāo)注的數(shù)組來(lái)工作。它使用最佳匹配算法來(lái)分配內(nèi)存,與heap_1不同,它確實(shí)允許釋放內(nèi)存。同樣,數(shù)組是靜態(tài)聲明的,因此將使應(yīng)用程序可能消耗大量RAM,甚至在數(shù)組的任何內(nèi)存被分配之前。最佳擬合算法確保pvPortMalloc()使用大小最接近請(qǐng)求字節(jié)數(shù)的空閑內(nèi)存塊。例如,考慮以下場(chǎng)景:
- 堆包含三個(gè)可用內(nèi)存塊,分別為5字節(jié)、25字節(jié)和100字節(jié)。
- pvPortMalloc()以請(qǐng)求20字節(jié)的RAM。
適合最小內(nèi)存請(qǐng)求的內(nèi)存塊是25字節(jié)塊,所以在返回一個(gè)20字節(jié)塊的指針前,pvPortMalloc()25字節(jié)塊分割分成一個(gè)20字節(jié)和一個(gè)5 字節(jié)塊。新的5字節(jié)塊仍然留作將來(lái)其他分配對(duì)pvPortMalloc()的調(diào)用。
與heap_4不同,Heap_2不將相鄰的自由塊組合成一個(gè)更大的塊,所以它更容易破碎。但是,如果分配和隨后釋放的塊總是相同的大小,碎片不是問(wèn)題。Heap_2適用于重復(fù)創(chuàng)建和刪除任務(wù)的應(yīng)用程序,前提是分配給已創(chuàng)建任務(wù)的堆棧的大小不會(huì)更改。
圖6演示了最佳匹配算法在創(chuàng)建、刪除任務(wù),然后再次創(chuàng)建任務(wù)時(shí),是如何工作。請(qǐng)參見(jiàn)圖6:
- A顯示了在創(chuàng)建了三個(gè)任務(wù)后的數(shù)組。一個(gè)很大的自由塊仍然保留在數(shù)組的頂部。
- B顯示了刪除其中一個(gè)任務(wù)后的數(shù)組。位于數(shù)組頂部的較大的自由塊保留了下來(lái)。現(xiàn)在還有兩個(gè)較小的空閑塊以前分配給TCB和刪除任務(wù)的堆棧
- C表示在創(chuàng)建另一個(gè)任務(wù)后的情況。創(chuàng)建任務(wù)導(dǎo)致了對(duì)pvportMalloc()的兩個(gè)調(diào)用,一個(gè)用于分配一個(gè)新的TCB,另一個(gè)用于分配任務(wù)棧。任務(wù)是使用xTaskCreate()API函數(shù)創(chuàng)建的,詳細(xì)描述見(jiàn)第3.4節(jié)。對(duì)pvPortMalloc()的調(diào)用發(fā)生在xTaskCreate()的內(nèi)部。
每個(gè)TCB的大小完全相同,因此最佳擬合算法確保先前分配給已刪除任務(wù)的TCB的RAM塊被重用,以分配新任務(wù)的TCB。分配給新創(chuàng)建的任務(wù)的堆棧大小與分配給之前刪除的任務(wù)的棧大小相同,因此最佳匹配算法可以確保重用之前分配給已刪除任務(wù)的棧的內(nèi)存塊來(lái)分配新任務(wù)的棧。位于數(shù)組頂部的較大的未分配塊保持不變。
Heap_2不是確定性的,但比大多數(shù)malloc()和free()的標(biāo)準(zhǔn)庫(kù)執(zhí)行都要快。
Heap_3
Heap_3.c使用標(biāo)準(zhǔn)庫(kù)malloc()和free()函數(shù),因此堆的大小由鏈接器配置確定,而configTOTAL_HEAP_SIZE設(shè)置沒(méi)有影響。Heap_3通過(guò)暫時(shí)暫停FreeRTOS調(diào)度程序,使malloc()和free()線程安全。線程安全,和調(diào)度程序暫停,都是在第7章,資源管理中討論的主題。
Heap_4
和heap_1和heap_2一樣,heap_4的工作原理是將一個(gè)數(shù)組細(xì)分為更小的塊。與前面一樣,數(shù)組是靜態(tài)聲明的,并由configTOTAL_HEAP_SIZE標(biāo)注,因此將使應(yīng)用程序似乎消耗大量RAM,甚至在實(shí)際從數(shù)組分配任何內(nèi)存之前。
==Heap_4使用第一個(gè)擬合算法來(lái)分配內(nèi)存。與heap_2不同,heap_4將合并相鄰的空閑內(nèi)存塊成為一個(gè)更大的內(nèi)存塊,從而將內(nèi)存碎片化的風(fēng)險(xiǎn)降到最低。==第一個(gè)擬合算法確保pvPortMalloc()使用第一個(gè)足夠大空閑內(nèi)存塊來(lái)確保滿足請(qǐng)求的字節(jié)數(shù)。例如,考慮以下場(chǎng)景:
- 堆包含三個(gè)可用內(nèi)存塊,按照它們?cè)跀?shù)組中出現(xiàn)的順序,分別為5字節(jié)、200字節(jié)和100字節(jié)。
- pvPortMalloc()以請(qǐng)求20字節(jié)的RAM。
第一個(gè)適合請(qǐng)求的自由RAM塊是200字節(jié)塊,因此pvPortMalloc()將200字節(jié)塊分成一個(gè)20字節(jié)塊和一個(gè)180字節(jié)塊,然后返回一個(gè)指向20字節(jié)塊的指針。新的180字節(jié)塊仍然可用于未來(lái)調(diào)用pvPortMalloc()。Heap_4將相鄰的自由塊合并成一個(gè)更大的塊,最大限度地減少了碎片化的風(fēng)險(xiǎn),并使其適合于重復(fù)分配和釋放不同大小的RAM塊的應(yīng)用程序。
圖7展示了在分配和釋放內(nèi)存時(shí),heap_4匹配算法合并內(nèi)存是如何工作的。參見(jiàn)圖7:
- A顯示了在創(chuàng)建了三個(gè)任務(wù)后的數(shù)組。一個(gè)很大的自由塊仍然保留在數(shù)組的頂部。
- B顯示刪除其中一個(gè)任務(wù)后的數(shù)組。大的自由塊在數(shù)組的頂部還在。還有一個(gè)空閑塊,是之前分配各被刪除任務(wù)的TCB和棧。注意,不像當(dāng)heap_2,刪除TCB和棧被刪除時(shí)釋放的內(nèi)存,不再作為兩個(gè)獨(dú)立的空閑塊,而是組合成一個(gè)新的自由塊。
- C顯示創(chuàng)建了一個(gè)FreeRTOS隊(duì)列后的情況。隊(duì)列使用xQueueCreate()API函數(shù)創(chuàng)建,詳見(jiàn)第4.3節(jié)。xQueueCreate()調(diào)用pvPortMalloc()來(lái)分配隊(duì)列使用的RAM。由于heap_4使用第一個(gè)擬合算法,pvPortMalloc()將從第一個(gè)足以保存隊(duì)列的空閑RAM塊中進(jìn)行分配,即圖7中刪除任務(wù)時(shí)釋放的RAM。但是,該隊(duì)列并不消耗空閑塊中的所有RAM,因此該塊被分成兩個(gè),并且未使用的部分仍然可用于將來(lái)調(diào)用pvPortMalloc()時(shí)進(jìn)行分配。
- D顯示了應(yīng)用程序代碼直接調(diào)用pvPortMalloc()后,而不是通過(guò)調(diào)用FreeRTOS API函數(shù)間接調(diào)用的情況。用戶程序所需要分配的空間足夠小,第一個(gè)自由塊(隊(duì)列和下一個(gè)TCB之間)就可以滿足。當(dāng)任務(wù)被刪除時(shí)釋放的內(nèi)存現(xiàn)在已經(jīng)被分成三個(gè)單獨(dú)的塊;第一個(gè)塊保存隊(duì)列,第二個(gè)塊保存用戶分配的內(nèi)存,第三個(gè)塊保持空閑。
- E顯示隊(duì)列被刪除后的情況,這會(huì)自動(dòng)釋放已分配給隊(duì)列的內(nèi)存。現(xiàn)在在用戶分配的塊的兩邊都有空閑內(nèi)存。
- F顯示了用戶分配的內(nèi)存也被釋放后的情況。用戶分配的塊所使用的內(nèi)存與兩邊的空閑內(nèi)存相結(jié)合,以創(chuàng)建一個(gè)更大的單個(gè)空閑塊。
Heap_4不是確定性的,但比大多數(shù)malloc()和free()的標(biāo)準(zhǔn)庫(kù)執(zhí)行都要快
設(shè)置Heap_4所使用的數(shù)組的起始地址
本節(jié)包含高級(jí)級(jí)別的信息。使用Heap_4并不需要閱讀或理解本節(jié)。
有時(shí),應(yīng)用程序作者需要將heap_4使用的數(shù)組放置在一個(gè)特定的內(nèi)存地址上。例如,FreeRTOS任務(wù)使用的棧是從堆中分配的,因此可能有必要確保堆位于快速的內(nèi)部?jī)?nèi)存中,而不是緩慢的外部?jī)?nèi)存中。
默認(rèn)情況下,heap_4使用的數(shù)組在heap_4.c源文件中聲明,其起始地址由鏈接器自動(dòng)設(shè)置。但是,如果在FreeRTOSConfig.h中將configAPPLICATION_ALLOCATED_HEAP編譯時(shí)配置常量設(shè)置為1,那么該數(shù)組必須由使用FreeRTOS的應(yīng)用程序聲明。如果該數(shù)組被聲明為應(yīng)用程序的一部分,那么應(yīng)用程序作者就可以設(shè)置其起始地址。
如果在FreeRTOSConfig.h中configAPPLICATION_ALLOCATED_HEAP設(shè)置為1,那么應(yīng)用程序的源文件中必須聲明一個(gè)名為ucHeap的uint8_t數(shù)組,并由configTOTAL_HEAP_SIZE設(shè)置大小。
將變量放置在特定內(nèi)存地址所需的語(yǔ)法取決于正在使用的編譯器,因此請(qǐng)參閱編譯器的文檔。下面是兩個(gè)編譯器的示例:
- 示例2顯示了GCC編譯器聲明該數(shù)組所需的語(yǔ)法,并將該數(shù)組放在一個(gè)名為.my_heap的內(nèi)存塊中。
- 示例3顯示了IAR編譯器聲明該數(shù)組所需的語(yǔ)法,并將該數(shù)組放在絕對(duì)內(nèi)存地址0x20000000處。
Heap_5
heap_5用于分配和釋放內(nèi)存的算法與heap_4相同。與heap_4不同的是,heap_5并不局限于從單個(gè)靜態(tài)的數(shù)組中分配內(nèi)存;heap_5可以從多個(gè)獨(dú)立且分散的內(nèi)存空間中分配內(nèi)存。當(dāng)RAM在系統(tǒng)的內(nèi)存表中不顯示為單個(gè)連續(xù)塊時(shí),heap_5將非常有用。
在編寫代碼時(shí),heap_5是唯一在調(diào)用pvPortMalloc()前必須顯式的初始化的內(nèi)存分配方法。Heap_5使用vPortDefineHeapRegions()API函數(shù)初始化。當(dāng)使用heap_5時(shí),必須在創(chuàng)建任何內(nèi)核對(duì)象(任務(wù)、隊(duì)列、信號(hào)量等)之前調(diào)用vPortDefineHeapRegions()。
vPortDefineHeapRegions() API函數(shù)
vPortDefineHeapRegions()用于指定每個(gè)單獨(dú)內(nèi)存區(qū)域的起始地址和大小,這些區(qū)域共同構(gòu)成了heap_5所使用的總內(nèi)存。
//The vPortDefineHeapRegions() API function prototype void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );每個(gè)單獨(dú)的存儲(chǔ)區(qū)域都由HeapRegion_t類型的結(jié)構(gòu)體來(lái)描述。所有可用內(nèi)存區(qū)域的描述作為HeapRegion_t結(jié)構(gòu)體組成的數(shù)組傳遞到vPortDefineHeapRegions()中。
//The HeapRegion_t structure /*返回值pxHeapRegions:指向HeapRegion_t結(jié)構(gòu)體組成的數(shù)組開(kāi)頭的指針。數(shù)組中的每個(gè)結(jié)構(gòu)體都描述了在使用heap_5時(shí)將成為堆的一部分的內(nèi)存區(qū)域的起始地址和長(zhǎng)度。數(shù)組中的HeapRegion_t結(jié)構(gòu)體必須按起始地址排序;描述具有最低起始地址的內(nèi)存區(qū)域的HeapRegion_t結(jié)構(gòu)必須是數(shù)組中的第一個(gè)結(jié)構(gòu),而描述具有最高起始地址的內(nèi)存區(qū)域的HeapRegion_t結(jié)構(gòu)必須是數(shù)組中的最后一個(gè)結(jié)構(gòu)。數(shù)組的末端由一個(gè)HeapRegion_t結(jié)構(gòu)標(biāo)記,該結(jié)構(gòu)的啟動(dòng)起始地址成員設(shè)置為NULL。*/ typedef struct HeapRegion {/* The start address of a block of memory that will be part of the heap.*/uint8_t *pucStartAddress;/* The size of the block of memory in bytes. */size_t xSizeInBytes; } HeapRegion_t;舉例來(lái)說(shuō),考慮圖8A中所示的假設(shè)內(nèi)存映射,它包含三個(gè)獨(dú)立的RAM塊:RAM1、RAM2和RAM3。假設(shè)可執(zhí)行代碼被放置在只讀內(nèi)存中,但沒(méi)有顯示。
示例6顯示了一個(gè)HeapRegion_t結(jié)構(gòu)體數(shù)組,它們一起描述了三個(gè)RAM塊。
雖然示例6正確地描述了RAM,但它沒(méi)有演示一個(gè)可用的示例,因?yàn)樗鼘⑺蠷AM分配給堆,沒(méi)有留下任何RAM供其他變量使用。
當(dāng)編譯項(xiàng)目時(shí),編譯過(guò)程的鏈接階段將為每個(gè)變量分配一個(gè)RAM地址。鏈接器可供使用的RAM通常由鏈接器配置文件來(lái)描述,例如鏈接器腳本。在圖8B中,假設(shè)鏈接器腳本包含了關(guān)于RAM1的信息,但不包含關(guān)于RAM2或RAM3的信息。因此,鏈接器在RAM1中放置了變量,只留下RAM1中地址0x0001 nnnn以上的部分可供heap_5使用。0x0001 nnnn的實(shí)際值將取決于被鏈接的應(yīng)用程序中所包含的所有變量的組合大小。鏈接器使所有RAM2和所有RAM3都未使用,使得整個(gè)RAM2和整個(gè)RAM3可供heap_5使用。
如果使用了示例6中所示的代碼,那么在地址0x0001 nnnn下面分配給heap_5的RAM將與用于保存變量的RAM重疊。為了避免這種情況,xHeapRegions[]數(shù)組中的第一個(gè)HeapRegion_t結(jié)構(gòu)可以使用起始地址0x0001 nnnn,而不是起始地址0x00010000。但是,這并不是一個(gè)推薦的解決方案,因?yàn)?#xff1a;
- 起始地址可能不容易確定。
- 鏈接器所使用的RAM的數(shù)量可能會(huì)在未來(lái)的構(gòu)建中發(fā)生變化,因此需要更新到HeapRegion_t結(jié)構(gòu)中所使用的起始地址。
- 如果鏈接器使用的RAM和heap_5使用的RAM重疊,構(gòu)建工具將不知道,因此不能警告應(yīng)用程序作者。
示例7展示了一個(gè)更方便和可維護(hù)的示例。它聲明了一個(gè)名為ucHeap的數(shù)組。ucHeap是一個(gè)普通變量,因此它成為鏈接器分配給RAM1的數(shù)據(jù)的一部分。xHeapRegion數(shù)組中的第一個(gè)HeapRegion_t結(jié)構(gòu)描述了ucHeap的起始地址和大小,因此ucHeap成為由heap_5管理的內(nèi)存的一部分。ucHeap的大小可以增加,直到連接器使用的RAM消耗掉了所有的RAM1,如圖8C所示。
//Listing 7. An array of HeapRegion_t structures that describe all of RAM2, all of RAM3, but only part of RAM1 /* Define the start address and size of the two RAM regions not used by the linker. */ #define RAM2_START_ADDRESS ( ( uint8_t * ) 0x00020000 ) #define RAM2_SIZE ( 32 * 1024 ) #define RAM3_START_ADDRESS ( ( uint8_t * ) 0x00030000 ) #define RAM3_SIZE ( 32 * 1024 ) /* Declare an array that will be part of the heap used by heap_5. The array will be placed in RAM1 by the linker. */ #define RAM1_HEAP_SIZE ( 30 * 1024 ) static uint8_t ucHeap[ RAM1_HEAP_SIZE ]; /* Create an array of HeapRegion_t definitions. Whereas in Listing 6 the first entry described all of RAM1, so heap_5 will have used all of RAM1, this time the first entry only describes the ucHeap array, so heap_5 will only use the part of RAM1 that contains the ucHeap array. The HeapRegion_t structures must still appear in start address order, with the structure that contains the lowest start address appearing first. */ const HeapRegion_t xHeapRegions[] = {{ ucHeap, RAM1_HEAP_SIZE },{ RAM2_START_ADDRESS, RAM2_SIZE },{ RAM3_START_ADDRESS, RAM3_SIZE },{ NULL, 0 } /* Marks the end of the array. */ };示例7中展示的技術(shù)的有如下優(yōu)點(diǎn):
- 不需要使用硬編碼的起始地址。
- 在HeapRegion_t結(jié)構(gòu)中使用的地址將由鏈接器自動(dòng)設(shè)置,因此將始終是正確的,即使鏈接器使用的RAM數(shù)量在未來(lái)的編譯中發(fā)生變化。
- 分配給heap_5的RAM不可能將由鏈接器放置到RAM1中的數(shù)據(jù)重疊。
- 如果ucHeap的大小太大,則該應(yīng)用程序?qū)⒉粫?huì)進(jìn)行鏈接。
Heap相關(guān)實(shí)用程序功能
The xPortGetFreeHeapSize() API Function
xPortGetFreeHeapSize()API函數(shù)返回在調(diào)用該函數(shù)時(shí)堆中的空閑字節(jié)數(shù)。它可以用于優(yōu)化堆的大小。例如,如果xPortGetFreeHeapSize()在創(chuàng)建了所有內(nèi)核對(duì)象后返回2000,那么configTOTAL_HEAP_SIZE的值可以減少到2000。當(dāng)使用heap_3時(shí),xPortGetFreeHeapSize()不可用。
/*返回值size_t:調(diào)用xPortGetFreeHeapSize()時(shí)堆中未分配的字節(jié)數(shù)。*/ size_t xPortGetFreeHeapSize( void );The xPortGetMinimumEverFreeHeapSize() API Function
xPortGetMinimumEverFreeHeapSize()API函數(shù)返回自FreeRTOS應(yīng)用程序開(kāi)始執(zhí)行以來(lái)堆中存在的最小未分配的最小字節(jié)數(shù)。函數(shù)的返回值表明應(yīng)用程序離堆內(nèi)存溢出有多近。例如,如果返回值為200,那么,在應(yīng)用程序開(kāi)始執(zhí)行后的某個(gè)時(shí)候,它距離堆空間耗盡不到200字節(jié)。
xPortGetMinimumEverFreeHeapSize()只有在使用heap_4或heap_5時(shí)才可用。
宏失效回調(diào)函數(shù)
可以直接從應(yīng)用程序代碼中調(diào)用pvPortMalloc()。在FreeRTOS源文件中,每次創(chuàng)建內(nèi)核對(duì)象(任務(wù)、隊(duì)列、信號(hào)量和事件組)時(shí)都會(huì)調(diào)用它。
就像標(biāo)準(zhǔn)庫(kù)Malloc()函數(shù)一樣,如果pvPortMalloc()因?yàn)檎?qǐng)求的塊大小不存在,則將返回NULL。如果因?yàn)閼?yīng)用程序作者正在創(chuàng)建一個(gè)內(nèi)核對(duì)象而執(zhí)行pvPortMalloc(),并且返回NULL,則該內(nèi)核對(duì)象將無(wú)法創(chuàng)建。所有示例堆分配方案都可以配置一個(gè)回調(diào)函數(shù),當(dāng)pvPortMalloc()返回為NULL時(shí)將調(diào)用該回調(diào)函數(shù)。
如果在FreeRTOSConfig.h中將configUSE_MALLOC_FAILED_HOOK設(shè)置為1,則應(yīng)用程序必須提供一個(gè)宏失效回調(diào)函數(shù),該函數(shù)具有示例10所示的名稱和原型。該函數(shù)可以以任何適合于該應(yīng)用程序的方式來(lái)實(shí)現(xiàn)。
總結(jié)
以上是生活随笔為你收集整理的FreeRTOS 教程指南 学习笔记 第二章 内存管理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 计算机视觉引论
- 下一篇: eplan2.5安装教程