0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

聊聊FreeRTOS內(nèi)存管理方案及相關(guān)的優(yōu)化措施

冬至子 ? 來源:劍一二 ? 作者:劍一二 ? 2023-11-13 11:28 ? 次閱讀

FreeRTOS 作為一個(gè)嵌入式實(shí)時(shí)操作系統(tǒng),其運(yùn)行的環(huán)境一般資源有限,特別是其內(nèi)存資源,可能只有幾 M,甚至是幾十 KB。針對不同的應(yīng)用場景,F(xiàn)reeRTOS 源碼中提供了 5 種內(nèi)存管理方案。本文就來聊聊這些內(nèi)存管理方案以及相關(guān)的優(yōu)化措施。

內(nèi)存管理相關(guān)的代碼在 lib/FreeRTOS/portable/MemMang 目錄下,從 heap_1.c 到 heap_5.c,文件相互獨(dú)立,并提供統(tǒng)一接口

void *pvPortMalloc( size_t xWantedSize );
void vPortFree( void *pv );

而 heap 的定義,可以是一塊靜態(tài)分配的數(shù)組作為 heap,也可以是指定的一塊區(qū)域作為 heap,本文就以靜態(tài)數(shù)組為例進(jìn)行講解。

#if( configAPPLICATION_ALLOCATED_HEAP == 1)
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif

實(shí)際上,內(nèi)存釋放接口并不一定都有效,因?yàn)?heap_1 方案是不支持內(nèi)存釋放的。在 heap_1 中調(diào)用 pvPortMalloc() 的時(shí)候,其具體實(shí)現(xiàn)如下所示:

圖片

pucAligendHeap: 對齊后的 heap 起始地址。

xNextFreeByte: 記錄下一次分配內(nèi)存的偏移值,實(shí)際就是已分配內(nèi)存的總大小。

pvReturn = pucAlignedHeap + xNextFreeByte;
xNextFreeByte += xWantedSize;

每次分配的時(shí)候,返回 pucAligendHeap + xNextFreeByte 的地址,然后更新 xNextFreeByte。當(dāng)然了,整個(gè)分配過程中,還有一些對齊操作。由于分配后的內(nèi)存不能進(jìn)行釋放,所以這種分配方案適用于不需要頻繁申請釋放內(nèi)存的場景。

相對于 heap_1 方案,在 heap_2 中增加了內(nèi)存釋放的函數(shù)。其實(shí)現(xiàn)是在每一次分配的內(nèi)存中增加了一些描述信息(也就是多分配一個(gè)結(jié)構(gòu)體大小的內(nèi)存,用于保存描述信息),這樣在釋放的時(shí)候,就可以根據(jù)這些信息回收內(nèi)存。

typedef struct A_BLOCK_LINK {
    struct A_BLOCK_LINK *pxNextFreeBlock;
    size_t xBlockSize;
} BlockLink_t;

static const uint16_t heapSTRUCT_SIZE = (( sizeof( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 )) & ~portBYTE_ALIGNMENT_MASK );

xWantedSize += heapSTRUCT_SIZE;  /*每次分配的時(shí)候自動(dòng)加上描述結(jié)構(gòu)體的大小*/

描述信息結(jié)構(gòu)體中包含了一個(gè)鏈表指針,用于在釋放的時(shí)候,將該內(nèi)存加入到空閑鏈表中,每次分配內(nèi)存實(shí)際就是從空閑鏈表上進(jìn)行分配(初始狀態(tài),整個(gè)鏈表除了頭尾,就一個(gè)成員,就是整個(gè)空閑的 heap 塊)。xBlockSize 用于描述分配內(nèi)存的大小。

圖片

其中,xStart 和 xEnd 是空閑鏈的頭尾節(jié)點(diǎn)。初始狀態(tài)時(shí),整個(gè) heap 就是一個(gè)空閑塊。分配的時(shí)候,從頭遍歷空閑鏈,找到第一個(gè)滿足大小的塊,也就是最先匹配原則(first fit),從中分裂出所需的大小,然后將剩余的插入到空閑鏈中。而釋放就是將其插入到空閑鏈中。

需要注意的是,插入空閑鏈中,都是升序排列的,也就是說在分配的時(shí)候,最先滿足的塊也是最優(yōu)的塊(best fit),可以減少碎片的產(chǎn)生。從實(shí)現(xiàn)來看,heap_2 雖然添加了釋放內(nèi)存函數(shù),但其在插入到空閑鏈的時(shí)候,沒有對相鄰的塊進(jìn)行合并,所以 heap_2 適用于操作固定大小內(nèi)存的場景。

heap_3 的方案沒有基于 heap_2 進(jìn)行優(yōu)化,而是直接使用 libc 庫中 malloc() / free() 接口,所以這里就不多做介紹。

事實(shí)上,相鄰塊合并功能是在 heap_4 中引入的。在將空閑塊插入到鏈表的時(shí)候,會(huì)判斷是否有空閑塊是相鄰的,如果相鄰就合并成一個(gè)更大的空閑塊,就能減少碎片的產(chǎn)生,進(jìn)而更適用于一般的內(nèi)存分配和釋放場景。注意 heap_4 在插入空閑鏈的時(shí)候,不再是升序排列,而是根據(jù)地址大小進(jìn)行排列,這樣便于判斷量表中前后兩個(gè)塊是否相鄰。

圖片

heap_5 相對于 heap_4 方案并沒有進(jìn)行算法上的優(yōu)化,它添加了一個(gè)接口可以指定某個(gè)內(nèi)存塊作為 heap。

void vPortDefineHeapRegions( const HeapRegion_t * const pRegions );

在前幾種內(nèi)存管理方案中,除了 heap_3,內(nèi)存塊都是靜態(tài)分配的。而在 heap_5 中 heap 不再是靜態(tài)定義的全局變量,而是需要顯示指定的一塊內(nèi)存區(qū)域作為 heap。這樣的好處就是,heap 的來源更加靈活,可以是和運(yùn)行空間不連續(xù)的一塊內(nèi)存,也可以將多個(gè)不連續(xù)的內(nèi)存塊作為 heap 進(jìn)行管理。

以上就介紹了 FreeRTOS 中原生的 5 種內(nèi)存管理方案??梢钥闯?,后面的內(nèi)存管理方案對前面的管理方案是兼容的,比如 heap_5 可以替代 heap_1, heap_2 和 heap_4 方案。這里可能就有個(gè)疑問了,為什么不直接用 heap_5 方案呢?更豐富的功能,意味著復(fù)雜的一些實(shí)現(xiàn),在一些簡單的場景中,heap_5 分配速度可能沒有 heap_1 或者 heap_2 來得快,所以 heap_1,heap_2 等方案也有其存在的意義。

在實(shí)際場景中,比如隨機(jī)的分配和釋放,而且分配的大小也不一致,這個(gè)時(shí)候,一般會(huì)選擇 heap_4。heap_4 引入了內(nèi)存合并功能,可以減少內(nèi)存碎片,但和 heap_2 相比,也把最優(yōu)匹配的原則去掉了。如下圖所示:

圖片

當(dāng)要分配一個(gè) 32Bytes 的內(nèi)存(經(jīng)過對齊等處理后的大?。?,按照 heap_4 的分配方案,最先匹配原則,會(huì)從 56Bytes 大小的塊中分配一個(gè)32Bytes 出去,而不是從第二個(gè)空閑塊,剛剛好是 32Bytes 的塊中分配。這樣的分配方法就會(huì)產(chǎn)生碎片。碎片多了就會(huì)導(dǎo)致空閑內(nèi)存看似很多,但大的內(nèi)存塊已經(jīng)沒有了。

優(yōu)化的辦法也很簡單,就是遍歷整個(gè)空閑鏈表,找到最優(yōu)的一個(gè)塊,其修改方法就是在原來 first fit 的基礎(chǔ)上,遍歷剩余的鏈表:

/* first fit */
while ((pxBlock- >xBlockSize < xWantedSize) && (pxBlock- >pxNextFreeBlock != NULL))
{
   pxPreviousBlock = pxBlock;
   pxBlock = pxBlock- >pxNextFreeBlock;
}
/* best fit */
BlockLink_t *pxTmp = pxBlock;
BlockLink_t *pxPreTmp = pxPreviousBlock;while (pxTmp != pxEnd){
   if ((pxTmp- >xBlockSize >= xWantedSize) && (pxTmp- >xBlockSize < pxBlock- >xBlockSize))
  {
       pxBlock = pxTmp;
       pxPreviousBlock = pxPreTmp;
  }
   pxPreTmp = pxTmp;
   pxTmp = pxTmp- >pxNextFreeBlock;
}

最優(yōu)匹配的引入,不是一定就比原來的方案效果要好,因?yàn)楸闅v整個(gè)空閑鏈表,會(huì)導(dǎo)致分配內(nèi)存的時(shí)間變長。這在某些對實(shí)時(shí)要求較高的環(huán)境中就不適應(yīng)了,比如在一些網(wǎng)絡(luò)環(huán)境中可能會(huì)因?yàn)槌瑫r(shí)而導(dǎo)致功能不正常。

其實(shí)分配快,內(nèi)存碎片少的方法有很多,只是他們的實(shí)現(xiàn)成本會(huì)有所不同,一個(gè)算法不可能適用所有的場景,分配方案中也沒有哪個(gè)比哪個(gè)一定更好,找到適合應(yīng)用場景的就是好的算法。這也許就是 FreeRTOS 中保留了多種分配方案的原因吧。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
收藏 人收藏

    評論

    相關(guān)推薦

    FreeRTOS內(nèi)存機(jī)制詳解

    FreeRTOS是一種實(shí)時(shí)操作系統(tǒng),它提供了多種內(nèi)存分配方式,包括動(dòng)態(tài)內(nèi)存分配和靜態(tài)內(nèi)存分配。
    的頭像 發(fā)表于 12-31 16:49 ?2335次閱讀
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>內(nèi)存</b>機(jī)制詳解

    第28章 FreeRTOS動(dòng)態(tài)內(nèi)存管理

    28.4 實(shí)驗(yàn)例程說明28.5總結(jié)28.1動(dòng)態(tài)內(nèi)存管理介紹 FreeRTOS支持5種動(dòng)態(tài)內(nèi)存管理方案
    發(fā)表于 09-11 07:15

    FreeRTOS內(nèi)存管理的算法解析?

    關(guān)于FreeRTOS內(nèi)存管理,有人測試過它給定的算法么?會(huì)不會(huì)有內(nèi)存碎片的出現(xiàn),如果產(chǎn)品一直運(yùn)行,會(huì)不會(huì)出現(xiàn)內(nèi)存崩潰的情況。求證啊。目前用h
    發(fā)表于 07-30 11:39

    Freertos關(guān)于堆內(nèi)存管理相關(guān)資料分享

    內(nèi)存管理Malloc、Free防止內(nèi)存碎片Freertos
    發(fā)表于 12-27 08:12

    什么是內(nèi)存優(yōu)化?有那些優(yōu)化措施?

    什么是內(nèi)存優(yōu)化?有那些優(yōu)化措施
    發(fā)表于 01-14 06:22

    通信設(shè)備中內(nèi)存管理優(yōu)化

    通過對內(nèi)存管理的分析,提出了內(nèi)存優(yōu)化算法。該算法解決了通信設(shè)備中由于大量消息的發(fā)送導(dǎo)致內(nèi)存管理
    發(fā)表于 02-21 11:42 ?22次下載

    FreeRTOS代碼剖析之1:內(nèi)存管理Heap

    內(nèi)存管理是一個(gè)操作系統(tǒng)的重要組成部分之一,所有應(yīng)用程序都離不開操作系統(tǒng)的內(nèi)存管理。因此,在剖析FreeRTOS的內(nèi)核代碼之前,前對
    發(fā)表于 02-09 05:25 ?889次閱讀
    <b class='flag-5'>FreeRTOS</b>代碼剖析之1:<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>Heap

    FreeRTOS代碼剖析之3:內(nèi)存管理Heap

    FreeRTOS8.0.1的第三個(gè)模型Heap_3,可以說是最容易理解的一個(gè)內(nèi)存管理模型。因?yàn)樵谶@個(gè)模型里,FreeRTOS直接將標(biāo)準(zhǔn)C庫中的malloc()和free()進(jìn)行加工打
    發(fā)表于 02-09 05:30 ?392次閱讀

    嵌入式操作系統(tǒng)FreeRTOS內(nèi)存如何管理和堆

    嵌入式操作系統(tǒng)FreeRTOS內(nèi)存管理和堆
    的頭像 發(fā)表于 01-10 15:17 ?4595次閱讀
    嵌入式操作系統(tǒng)<b class='flag-5'>FreeRTOS</b><b class='flag-5'>內(nèi)存</b>如何<b class='flag-5'>管理</b>和堆

    FreeRTOS高級篇7---FreeRTOS內(nèi)存管理分析

    FreeRTOS操作系統(tǒng)將內(nèi)核與內(nèi)存管理分開實(shí)現(xiàn),操作系統(tǒng)內(nèi)核僅規(guī)定了必要的內(nèi)存管理函數(shù)原型,而不關(guān)心這些
    發(fā)表于 01-26 17:36 ?7次下載
    <b class='flag-5'>FreeRTOS</b>高級篇7---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>分析

    FreeRTOS系列第8篇---FreeRTOS內(nèi)存管理

    本文介紹內(nèi)存管理的基礎(chǔ)知識(shí),詳細(xì)源碼分析見《 FreeRTOS高級篇7---FreeRTOS內(nèi)存管理
    發(fā)表于 01-26 17:56 ?17次下載
    <b class='flag-5'>FreeRTOS</b>系列第8篇---<b class='flag-5'>FreeRTOS</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>

    FreeRTOS的源碼下載

    內(nèi)存管理文件在FreeRTOS\portable\MemMang文件夾下,FreeRTOS提供了五種內(nèi)存
    的頭像 發(fā)表于 02-10 11:00 ?3062次閱讀
    <b class='flag-5'>FreeRTOS</b>的源碼下載

    freeRTOS源碼中內(nèi)存管理方案

    freeRTOS源碼中提供了五種內(nèi)存管理方案,可以說是很方便了。實(shí)際需要使用哪一種,可以根據(jù)自己項(xiàng)目的需要進(jìn)行選擇,都是可以的。
    的頭像 發(fā)表于 02-10 11:11 ?691次閱讀

    FreeRTOS內(nèi)存管理簡介

    ,比如任務(wù)創(chuàng)建函數(shù) xTaskCreateStatic(),使用此函數(shù)創(chuàng)建任務(wù)的時(shí)候需要由用戶定義任務(wù)堆棧,我們不討論這種靜態(tài)方法。 使用動(dòng)態(tài)內(nèi)存管理的時(shí)候 FreeRTOS 內(nèi)核在創(chuàng)建任務(wù)、隊(duì)列、信號(hào)量的時(shí)候會(huì)動(dòng)態(tài)的申請 RA
    的頭像 發(fā)表于 07-30 10:26 ?618次閱讀

    FreeRTOS內(nèi)存管理實(shí)現(xiàn)

    FreeRTOS是一個(gè)為嵌入式系統(tǒng)設(shè)計(jì)的開源實(shí)時(shí)操作系統(tǒng)。它提供了一個(gè)多任務(wù)內(nèi)核和一系列功能,適合在資源受限的設(shè)備上管理實(shí)時(shí)任務(wù)和應(yīng)用程序。FreeRTOS內(nèi)存
    的頭像 發(fā)表于 10-10 16:17 ?824次閱讀
    <b class='flag-5'>FreeRTOS</b><b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理</b>實(shí)現(xiàn)