<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      15、內(nèi)存管理

      1、 內(nèi)存管理的基本概念

        在計(jì)算系統(tǒng)中,變量、中間數(shù)據(jù)一般存放在系統(tǒng)存儲(chǔ)空間中,只有在實(shí)際使用時(shí)才將它們從存儲(chǔ)空間調(diào)入到中央處理器內(nèi)部進(jìn)行運(yùn)算。通常存儲(chǔ)空間可以分為兩種:內(nèi)部存儲(chǔ)空間和外部存儲(chǔ)空間。內(nèi)部存儲(chǔ)空間訪問(wèn)速度比較快,能夠按照變量地址隨機(jī)地訪問(wèn),也就是我們通常所說(shuō)的 RAM(隨機(jī)存儲(chǔ)器),或電腦的內(nèi)存;而外部存儲(chǔ)空間內(nèi)所保存的內(nèi)容相對(duì)來(lái)說(shuō)比較固定,即使掉電后數(shù)據(jù)也不會(huì)丟失,可以把它理解為電腦的硬盤。

        FreeRTOS 操作系統(tǒng)將內(nèi)核與內(nèi)存管理分開實(shí)現(xiàn),操作系統(tǒng)內(nèi)核僅規(guī)定了必要的內(nèi)存管理函數(shù)原型,而不關(guān)心這些內(nèi)存管理函數(shù)是如何實(shí)現(xiàn)的,所以在 FreeRTOS 中提供了多種內(nèi)存分配算法(分配策略),但是上層接口( API)卻是統(tǒng)一的。這樣做可以增加系統(tǒng)的靈活性: 用戶可以選擇對(duì)自己更有利的內(nèi)存管理策略,在不同的應(yīng)用場(chǎng)合使用不同的內(nèi)存分配策略。

        在嵌入式程序設(shè)計(jì)中內(nèi)存分配應(yīng)該是根據(jù)所設(shè)計(jì)系統(tǒng)的特點(diǎn)來(lái)決定選擇使用動(dòng)態(tài)內(nèi)存分配還是靜態(tài)內(nèi)存分配算法,一些可靠性要求非常高的系統(tǒng)應(yīng)選擇使用靜態(tài)的,而普通的業(yè)務(wù)系統(tǒng)可以使用動(dòng)態(tài)來(lái)提高內(nèi)存使用效率。靜態(tài)可以保證設(shè)備的可靠性但是需要考慮內(nèi)存上限,內(nèi)存使用效率低,而動(dòng)態(tài)則是相反。

        FreeRTOS 內(nèi)存管理模塊管理用于系統(tǒng)中內(nèi)存資源,它是操作系統(tǒng)的核心模塊之一。主要包括內(nèi)存的初始化、分配以及釋放。

        很多人會(huì)有疑問(wèn),什么不直接使用 C 標(biāo)準(zhǔn)庫(kù)中的內(nèi)存管理函數(shù)呢?在電腦中我們可以用 malloc()和 free()這兩個(gè)函數(shù)動(dòng)態(tài)的分配內(nèi)存和釋放內(nèi)存。但是,在嵌入式實(shí)時(shí)操作系統(tǒng)中,調(diào)用 malloc()和 free()卻是危險(xiǎn)的,原因有以下幾點(diǎn):

      • 這些函數(shù)在小型嵌入式系統(tǒng)中并不總是可用的,小型嵌入式設(shè)備中的 RAM 不足。
      • 它們的實(shí)現(xiàn)可能非常的大,占據(jù)了相當(dāng)大的一塊代碼空間。
      • 他們幾乎都不是安全的。
      • 它們并不是確定的,每次調(diào)用這些函數(shù)執(zhí)行的時(shí)間可能都不一樣。
      • 它們有可能產(chǎn)生碎片。
      • 這兩個(gè)函數(shù)會(huì)使得鏈接器配置得復(fù)雜。
      • 如果允許堆空間的生長(zhǎng)方向覆蓋其他變量占據(jù)的內(nèi)存,它們會(huì)成為 debug 的災(zāi)難。

        在一般的實(shí)時(shí)嵌入式系統(tǒng)中,由于實(shí)時(shí)性的要求,很少使用虛擬內(nèi)存機(jī)制。所有的內(nèi)存都需要用戶參與分配,直接操作物理內(nèi)存,所分配的內(nèi)存不能超過(guò)系統(tǒng)的物理內(nèi)存,所有的系統(tǒng)堆棧的管理,都由用戶自己管理。

        同時(shí),在嵌入式實(shí)時(shí)操作系統(tǒng)中,對(duì)內(nèi)存的分配時(shí)間要求更為苛刻,分配內(nèi)存的時(shí)間必須是確定的。一般內(nèi)存管理算法是根據(jù)需要存儲(chǔ)的數(shù)據(jù)的長(zhǎng)度在內(nèi)存中去尋找一個(gè)與這段數(shù)據(jù)相適應(yīng)的空閑內(nèi)存塊,然后將數(shù)據(jù)存儲(chǔ)在里面。而尋找這樣一個(gè)空閑內(nèi)存塊所耗費(fèi)的時(shí)間是不確定的,因此對(duì)于實(shí)時(shí)系統(tǒng)來(lái)說(shuō),這就是不可接受的,實(shí)時(shí)系統(tǒng)必須要保證內(nèi)存塊的分配過(guò)程在可預(yù)測(cè)的確定時(shí)間內(nèi)完成,否則實(shí)時(shí)任務(wù)對(duì)外部事件的響應(yīng)也將變得不可確定。

        而在嵌入式系統(tǒng)中,內(nèi)存是十分有限而且是十分珍貴的,用一塊內(nèi)存就少了一塊內(nèi)存,而在分配中隨著內(nèi)存不斷被分配和釋放,整個(gè)系統(tǒng)內(nèi)存區(qū)域會(huì)產(chǎn)生越來(lái)越多的碎片,因?yàn)樵谑褂眠^(guò)程中,申請(qǐng)了一些內(nèi)存,其中一些釋放了,導(dǎo)致內(nèi)存空間中存在一些小的內(nèi)存塊,它們地址不連續(xù),不能夠作為一整塊的大內(nèi)存分配出去,所以一定會(huì)在某個(gè)時(shí)間,系統(tǒng)已經(jīng)無(wú)法分配到合適的內(nèi)存了,導(dǎo)致系統(tǒng)癱瘓。其實(shí)系統(tǒng)中實(shí)際是還有內(nèi)存的,但是因?yàn)樾K的內(nèi)存的地址不連續(xù),導(dǎo)致無(wú)法分配成功,所以我們需要一個(gè)優(yōu)良的內(nèi)存分配算法來(lái)避免這種情況的出現(xiàn)。

        不同的嵌入式系統(tǒng)具有不同的內(nèi)存配置和時(shí)間要求。所以單一的內(nèi)存分配算法只可能適合部分應(yīng)用程序。因此, FreeRTOS 將內(nèi)存分配作為可移植層面(相對(duì)于基本的內(nèi)核代碼部分而言), FreeRTOS 有針對(duì)性的提供了不同的內(nèi)存分配管理算法,這使得應(yīng)用于不同場(chǎng)景的設(shè)備可以選擇適合自身內(nèi)存算法。

        FreeRTOS 對(duì)內(nèi)存管理做了很多事情, FreeRTOS 的 V9.0.0 版本為我們提供了 5 種內(nèi)存管理算法,分別是 heap_1.c、 heap_2.c、 heap_3.c、 heap_4.c、 heap_5.c,源文件存放于FreeRTOS\Source\portable\MemMang 路徑下,在使用的時(shí)候選擇其中一個(gè)添加到我們的工程中去即可。

        FreeRTOS 的內(nèi)存管理模塊通過(guò)對(duì)內(nèi)存的申請(qǐng)、釋放操作,來(lái)管理用戶和系統(tǒng)對(duì)內(nèi)存的使用,使內(nèi)存的利用率和使用效率達(dá)到最優(yōu),同時(shí)最大限度地解決系統(tǒng)可能產(chǎn)生的內(nèi)存碎片問(wèn)題。

      2、 內(nèi)存管理的應(yīng)用場(chǎng)景

        首先,在使用內(nèi)存分配前,必須明白自己在做什么,這樣做與其他的方法有什么不同,特別是會(huì)產(chǎn)生哪些負(fù)面影響,在自己的產(chǎn)品面前,應(yīng)當(dāng)選擇哪種分配策略。

        內(nèi)存管理的主要工作是動(dòng)態(tài)劃分并管理用戶分配好的內(nèi)存區(qū)間,主要是在用戶需要使用大小不等的內(nèi)存塊的場(chǎng)景中使用, 當(dāng)用戶需要分配內(nèi)存時(shí),可以通過(guò)操作系統(tǒng)的內(nèi)存申請(qǐng)函數(shù)索取指定大小內(nèi)存塊,一旦使用完畢,通過(guò)動(dòng)態(tài)內(nèi)存釋放函數(shù)歸還所占用內(nèi)存,使之可以重復(fù)使用(heap_1.c 的內(nèi)存管理除外)。

        例如我們需要定義一個(gè) float 型數(shù)組: floatArr[];

        但是,在使用數(shù)組的時(shí)候,總有一個(gè)問(wèn)題困擾著我們:數(shù)組應(yīng)該有多大?在很多的情況下,你并不能確定要使用多大的數(shù)組,可能為了避免發(fā)生錯(cuò)誤你就需要把數(shù)組定義得足夠大。即使你知道想利用的空間大小,但是如果因?yàn)槟撤N特殊原因空間利用的大小有增加或者減少,你又必須重新去修改程序,擴(kuò)大數(shù)組的存儲(chǔ)范圍。這種分配固定大小的內(nèi)存分配方法稱之為靜態(tài)內(nèi)存分配。這種內(nèi)存分配的方法存在比較嚴(yán)重的缺陷,在大多數(shù)情況下會(huì)浪費(fèi)大量的內(nèi)存空間,在少數(shù)情況下,當(dāng)你定義的數(shù)組不夠大時(shí),可能引起下標(biāo)越界錯(cuò)誤,甚至導(dǎo)致嚴(yán)重后果。

        我們用動(dòng)態(tài)內(nèi)存分配就可以解決上面的問(wèn)題。所謂動(dòng)態(tài)內(nèi)存分配就是指在程序執(zhí)行的過(guò)程中動(dòng)態(tài)地分配或者回收存儲(chǔ)空間的分配內(nèi)存的方法。動(dòng)態(tài)內(nèi)存分配不象數(shù)組等靜態(tài)內(nèi)存分配方法那樣需要預(yù)先分配存儲(chǔ)空間,而是由系統(tǒng)根據(jù)程序的需要即時(shí)分配,且分配的大小就是程序要求的大小。

      3、 內(nèi)存管理方案詳解

        FreeRTOS 規(guī)定了內(nèi)存管理的函數(shù)接口,具體見,但是不管其內(nèi)部的內(nèi)存管理方案是怎么實(shí)現(xiàn)的,所以, FreeRTOS 可以提供多個(gè)內(nèi)存管理方案,下面,就一起看看各個(gè)內(nèi)存管理方案的區(qū)別。

      void *pvPortMalloc( size_t xSize ); //內(nèi)存申請(qǐng)函數(shù)
      void vPortFree( void *pv ); //內(nèi)存釋放函數(shù)
      void vPortInitialiseBlocks( void ); //初始化內(nèi)存堆函數(shù)
      size_t xPortGetFreeHeapSize( void ); //獲取當(dāng)前未分配的內(nèi)存堆大小
      size_t xPortGetMinimumEverFreeHeapSize( void ); //獲取未分配的內(nèi)存堆歷史最小值

        FreeRTOS 提供的內(nèi)存管理都是從內(nèi)存堆中分配內(nèi)存的。 從前面學(xué)習(xí)的過(guò)程中,我們也知道,創(chuàng)建任務(wù)、消息隊(duì)列、事件等操作都使用到分配內(nèi)存的函數(shù),這是系統(tǒng)中默認(rèn)使用內(nèi)存管理函數(shù)從內(nèi)存堆中分配內(nèi)存給系統(tǒng)核心組件使用。

        對(duì)于 heap_1.c、 heap_2.c 和 heap_4.c 這三種內(nèi)存管理方案,內(nèi)存堆實(shí)際上是一個(gè)很大的 數(shù) 組 , 定 義 為 static uint8_t ucHeap[ configTOTAL_HEAP_SIZE] , 而 宏 定 義configTOTAL_HEAP_SIZE 則表示系統(tǒng)管理內(nèi)存大小,單位為字, 在 FreeRTOSConfig.h 中由用戶設(shè)定。

        對(duì)于 heap_3.c 這種內(nèi)存管理方案, 它封裝了 C 標(biāo)準(zhǔn)庫(kù)中的 malloc()和 free()函數(shù),封裝后的 malloc()和 free()函數(shù)具備保護(hù),可以安全在嵌入式系統(tǒng)中執(zhí)行。因此, 用戶需要通過(guò)編譯器或者啟動(dòng)文件設(shè)置堆空間。

        heap_5.c 方案允許用戶使用多個(gè)非連續(xù)內(nèi)存堆空間,每個(gè)內(nèi)存堆的起始地址和大小由用戶定義。 這種應(yīng)用其實(shí)還是很大的,比如做圖形顯示、 GUI 等,可能芯片內(nèi)部的 RAM是不夠用戶使用的,需要外部 SDRAM,那這種內(nèi)存管理方案則比較合適。

        3.1、heap_1.c

        heap_1.c 管理方案是 FreeRTOS 提供所有內(nèi)存管理方案中最簡(jiǎn)單的一個(gè),它只能申請(qǐng)內(nèi)存而不能進(jìn)行內(nèi)存釋放,并且申請(qǐng)內(nèi)存的時(shí)間是一個(gè)常量,這樣子對(duì)于要求安全的嵌入式設(shè)備來(lái)說(shuō)是最好的,因?yàn)椴辉试S內(nèi)存釋放,就不會(huì)產(chǎn)生內(nèi)存碎片而導(dǎo)致系統(tǒng)崩潰,但是也有缺點(diǎn),那就是內(nèi)存利用率不高, 某段內(nèi)存只能用于內(nèi)存申請(qǐng)的地方,即使該內(nèi)存只使用一次,也無(wú)法讓系統(tǒng)回收重新利用。

        實(shí)際上,大多數(shù)的嵌入式系統(tǒng)并不會(huì)經(jīng)常動(dòng)態(tài)申請(qǐng)與釋放內(nèi)存,一般都是在系統(tǒng)完成的時(shí)候,就一直使用下去,永不刪除, 所以這個(gè)內(nèi)存管理方案實(shí)現(xiàn)簡(jiǎn)潔、安全可靠,使用的非常廣泛。

        heap1.c 方案具有以下特點(diǎn):

        (1)用于從不刪除任務(wù)、隊(duì)列、信號(hào)量、互斥量等的應(yīng)用程序(實(shí)際上大多數(shù)使用FreeRTOS 的應(yīng)用程序都符合這個(gè)條件) 。
       ?。?)函數(shù)的執(zhí)行時(shí)間是確定的并且不會(huì)產(chǎn)生內(nèi)存碎片。heap_1.c 管理方案使用兩個(gè)靜態(tài)變量對(duì)系統(tǒng)管理的內(nèi)存進(jìn)行跟蹤內(nèi)存分配,具體見代碼清單 23-2heap_1.c 靜態(tài)變量

      static size_t xNextFreeByte = ( size_t ) 0;
      static uint8_t *pucAlignedHeap = NULL;

        變量 xNextFreeByte 用來(lái)定位下一個(gè)空閑的內(nèi)存堆位置。 真正的運(yùn)作過(guò)程是記錄已經(jīng)被分配的內(nèi)存大小,在每次申請(qǐng)內(nèi)存成功后,都會(huì)增加申請(qǐng)內(nèi)存的字節(jié)數(shù)目。 因?yàn)閮?nèi)存堆實(shí)際上是一個(gè)大數(shù)組,我們只需要知道已分配內(nèi)存的大小,就可以用它作為偏移量找到未分配內(nèi)存的起始地址。
      靜態(tài)變量 pucAlignedHeap 是一個(gè)指向?qū)R后的內(nèi)存堆起始地址,我們使用一個(gè)數(shù)組作為堆內(nèi)存,但是數(shù)組的起始地址并不一定是對(duì)齊的內(nèi)存地址,所以我們需要得到FreeRTOS 管理的內(nèi)存空間對(duì)齊后的起始地址,并且保存在靜態(tài)變量 pucAlignedHeap 中。

        為什么要對(duì)齊?這是因?yàn)榇蠖鄶?shù)硬件訪問(wèn)內(nèi)存對(duì)齊的數(shù)據(jù)速度會(huì)更快。為了提高性能,F(xiàn)reeRTOS 會(huì)進(jìn)行對(duì)齊操作,不同的硬件架構(gòu)的內(nèi)存對(duì)齊操作可能不一樣,對(duì)于 Cortex-M3架構(gòu),進(jìn)行 8 字節(jié)對(duì)齊。

        下面一起來(lái)看看 heap_1.c 方案中的內(nèi)存管理相關(guān)函數(shù)的實(shí)現(xiàn)過(guò)程。

        (1)、內(nèi)存申請(qǐng)函數(shù) pvPortMalloc()

        內(nèi)存申請(qǐng)函數(shù)就是用于申請(qǐng)一塊用戶指定大小的內(nèi)存空間,當(dāng)系統(tǒng)管理的內(nèi)存空間滿足用戶需要的大小的時(shí)候,就能申請(qǐng)成功,并且返回內(nèi)存空間的起始地址。

      /*******************************************************************************************************
        *@ 函數(shù)功能:申請(qǐng)內(nèi)存
        *@ 函數(shù)參數(shù):xWantedSize:申請(qǐng)內(nèi)存大小
        *@ 返回值:無(wú)
      *******************************************************************************************************/
      void *pvPortMalloc( size_t xWantedSize );

        在 使 用 內(nèi) 存 申 請(qǐng) 函 數(shù) 之 前 , 需 要 將 管 理 的 內(nèi) 存 進(jìn) 行 初 始 化 , 需 要 將 變 量pucAlignedHeap 指向內(nèi)存域第一個(gè)地址對(duì)齊處,因?yàn)橄到y(tǒng)管理的內(nèi)存其實(shí)是一個(gè)大數(shù)組,而編譯器為這個(gè)數(shù)組分配的起始地址是隨機(jī)的,不一定符合系統(tǒng)的對(duì)齊要求,這時(shí)候要進(jìn)行內(nèi)存地址對(duì)齊操作。比如數(shù)組 ucHeap 的地址從 0x20000123 處開始,系統(tǒng)按照 8 字節(jié)對(duì)齊,則對(duì)齊后系統(tǒng)管理的內(nèi)存示意圖具體見下圖。

       在內(nèi)存對(duì)齊完成后, 用戶想要申請(qǐng)一個(gè) 30 字節(jié)大小的內(nèi)存,那么按照系統(tǒng)對(duì)齊的要求,我們會(huì)申請(qǐng)到 32 個(gè)字節(jié)大小的內(nèi)存空間,即使我們只需要 30 字節(jié)的內(nèi)存,申請(qǐng)完成的示意圖具體見下圖。

       ?。?)、 其他函數(shù)

        其實(shí) heap_1.c 方案還有一些其他函數(shù),只不過(guò)基本沒(méi)啥用,就簡(jiǎn)單說(shuō)說(shuō), vPortFree()這個(gè)函數(shù)其實(shí)上面都沒(méi)做,因?yàn)?heap_1.c 采用的內(nèi)存管理算法中不支持釋放內(nèi)存。vPortInitialiseBlocks()僅僅將靜態(tài)局部變量 xNextFreeByte 設(shè)置為 0,表示內(nèi)存沒(méi)有被申請(qǐng)。xPortGetFreeHeapSize()則是獲取當(dāng)前未分配的內(nèi)存堆大小, 這個(gè)函數(shù)通常用于檢查我們?cè)O(shè)置的內(nèi)存堆是否合理,通過(guò)這個(gè)函數(shù)可以估計(jì)出最壞情況下需要多大的內(nèi)存堆,以便合理的節(jié)省內(nèi)存資源。

        3.2、 heap_2.c

        heap_2.c 方案與 heap_1.c 方案采用的內(nèi)存管理算法不一樣,它采用一種最佳匹配算法(best fit algorithm),比如我們申請(qǐng) 100 字節(jié)的內(nèi)存,而可申請(qǐng)內(nèi)存中有三塊對(duì)應(yīng)大小 200 字節(jié), 500 字節(jié)和 1000 字節(jié)大小的內(nèi)存塊,按照算法的最佳匹配,這時(shí)候系統(tǒng)會(huì)把 200 字節(jié)大小的內(nèi)存塊進(jìn)行分割并返回申請(qǐng)內(nèi)存的起始地址,剩余的內(nèi)存則插回鏈表留待下次申請(qǐng)。

        Heap_2.c 方案支持釋放申請(qǐng)的內(nèi)存, 但是它不能把相鄰的兩個(gè)小的內(nèi)存塊合成一個(gè)大的內(nèi)存塊, 對(duì)于每次申請(qǐng)內(nèi)存大小都比較固定的,這個(gè)方式是沒(méi)有問(wèn)題的,而對(duì)于每次申請(qǐng)并不是固定內(nèi)存大小的則會(huì)造成內(nèi)存碎片, 后面要講解的 heap_4.c 方案采用的內(nèi)存管理算法能解決內(nèi)存碎片的問(wèn)題,可以把這些釋放的相鄰的小的內(nèi)存塊合并成一個(gè)大的內(nèi)存塊。

        同樣的,內(nèi)存分配時(shí)需要的總的內(nèi)存堆空間由文件 FreeRTOSConfig.h 中的宏configTOTAL_HEAP_SIZE 配置,單位為字。 通過(guò)調(diào)用函數(shù) xPortGetFreeHeapSize() 我們可以知道還剩下多少內(nèi)存沒(méi)有使用, 但是并不包括內(nèi)存碎片, 這樣一來(lái)我們可以實(shí)時(shí)的調(diào)整和優(yōu)化 configTOTAL_HEAP_SIZE 的大小。

        heap_2.c 方案具有以下特點(diǎn):

       ?。?)可以用在那些反復(fù)的刪除任務(wù)、隊(duì)列、信號(hào)量、等內(nèi)核對(duì)象且不擔(dān)心內(nèi)存碎片的應(yīng)用程序。
        (2)如果我們的應(yīng)用程序中的隊(duì)列、任務(wù)、信號(hào)量、 等工作在一個(gè)不可預(yù)料的順序,這樣子也有可能會(huì)導(dǎo)致內(nèi)存碎片。
        (3)具有不確定性,但是效率比標(biāo)準(zhǔn) C 庫(kù)中的 malloc 函數(shù)高得多
       ?。?)不能用于那些內(nèi)存分配和釋放是隨機(jī)大小的應(yīng)用程序。

        heap_2.c 方案與 heap_1 方案在內(nèi)存堆初始化的時(shí)候操作都是一樣的,在內(nèi)存中開辟了一個(gè)靜態(tài)數(shù)組作為堆的空間,大小由用戶定義,然后進(jìn)行字節(jié)對(duì)齊處理。heap_2.c 方案采用鏈表的數(shù)據(jù)結(jié)構(gòu)記錄空閑內(nèi)存塊,將所有的空閑內(nèi)存塊組成一個(gè)空閑內(nèi)存塊鏈表, FreeRTOS 采用 2 個(gè) BlockLink_t 類型的局部靜態(tài)變量 xStart、 xEnd 來(lái)標(biāo)識(shí)空閑內(nèi)存塊鏈表的起始位置與結(jié)束位置,空閑內(nèi)存塊鏈表結(jié)構(gòu)體具體見代碼清單 

      typedef struct A_BLOCK_LINK 
      {
          struct A_BLOCK_LINK *pxNextFreeBlock;
          size_t xBlockSize;
      } BlockLink_t;
      pxNextFreeBlock 成員變量是指向下一個(gè)空閑內(nèi)存塊的指針。
      xBlockSize 用于記錄申請(qǐng)的內(nèi)存塊的大小,包括鏈表結(jié)構(gòu)體大小。  

       ?。?)、內(nèi)存申請(qǐng)函數(shù) pvPortMalloc()

        heap_2.c 內(nèi)存管理方案采用最佳匹配算法管理內(nèi)存,系統(tǒng)會(huì)先從內(nèi)存塊空閑鏈表頭開始進(jìn)行遍歷,查找符合用戶申請(qǐng)大小的內(nèi)存塊(內(nèi)存塊空閑鏈表按內(nèi)存塊大小升序排列,所以最先返回的的塊一定是最符合申請(qǐng)內(nèi)存大小,所謂的最匹配算法就是這個(gè)意思來(lái)的)。當(dāng)找到內(nèi)存塊的時(shí)候, 返回該內(nèi)存塊偏移 heapSTRUCT_SIZE 個(gè)字節(jié)后的地址, 因?yàn)樵诿繅K內(nèi)存塊前面預(yù)留的節(jié)點(diǎn)是用于記錄內(nèi)存塊的信息, 用戶不需要也不允許操作這部分內(nèi)存。

        在申請(qǐng)內(nèi)存成功的同時(shí)系統(tǒng)還會(huì)判斷當(dāng)前這塊內(nèi)存是否有剩余(大于一個(gè)鏈表節(jié)點(diǎn)所需內(nèi)存空間), 這樣子就表示剩下的內(nèi)存塊還是能存放東西的,也要將其利用起來(lái)。 如果有剩余的內(nèi)存空間, 系統(tǒng)會(huì)將內(nèi)存塊進(jìn)行分割, 在剩余的內(nèi)存塊頭部添加一個(gè)內(nèi)存節(jié)點(diǎn),并且完善該空閑內(nèi)存塊的信息,然后將其按內(nèi)存塊大小插入內(nèi)存塊空閑鏈表中, 供下次分配使用, 其中 prvInsertBlockIntoFreeList() 這個(gè)函數(shù)就是把節(jié)點(diǎn)按大小插入到鏈表中。 

      /*******************************************************************************************************
        *@ 函數(shù)功能:申請(qǐng)內(nèi)存
        *@ 函數(shù)參數(shù):xWantedSize:申請(qǐng)內(nèi)存大小
        *@ 返回值:無(wú)
      *******************************************************************************************************/
      void *pvPortMalloc( size_t xWantedSize );

        (2)、內(nèi)存釋放函數(shù) vPortFree()

        分配內(nèi)存的過(guò)程簡(jiǎn)單,那么釋放內(nèi)存的過(guò)程更簡(jiǎn)單,只需要向內(nèi)存釋放函數(shù)中傳入要釋放的內(nèi)存地址,那么系統(tǒng)會(huì)自動(dòng)向前索引到對(duì)應(yīng)鏈表節(jié)點(diǎn), 并且取出這塊內(nèi)存塊的信息,將這個(gè)節(jié)點(diǎn)插入到空閑內(nèi)存塊鏈表中,將這個(gè)內(nèi)存塊歸還給系統(tǒng),下面來(lái)看看 vPortFree()的源碼,具體見代碼清單 。

      void vPortFree( void *pv )
      {
          uint8_t *puc = ( uint8_t * ) pv;
          BlockLink_t *pxLink;
      
          if ( pv != NULL ) 
          {
              /* 根據(jù)要釋放的內(nèi)存塊找到對(duì)應(yīng)的鏈表節(jié)點(diǎn) */
              puc -= heapSTRUCT_SIZE; (1)
      
              pxLink = ( void * ) puc;
      
              vTaskSuspendAll(); (2)
              {
                  /* 將要釋放的內(nèi)存塊添加到空閑鏈表 */
                  prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
                  /* 更新一下當(dāng)前的未分配的內(nèi)存大小 */
                  xFreeBytesRemaining += pxLink->xBlockSize; (3)
                  traceFREE( pv, pxLink->xBlockSize );
              }
              ( void ) xTaskResumeAll(); (4)
          }
      }
      代碼清單 23-7(1):根據(jù)要釋放的內(nèi)存塊進(jìn)行地址偏移找到對(duì)應(yīng)的鏈表節(jié)點(diǎn)。
      代碼清單 23-7(2):掛起調(diào)度器,內(nèi)存的操作都需要掛起調(diào)度器。
      代碼清單 23-7(3):將要釋放的內(nèi)存塊添加到空閑鏈表, prvInsertBlockIntoFreeList 是一個(gè)宏定義, 就是對(duì)鏈表的簡(jiǎn)單操作, 將釋放的內(nèi)存塊按內(nèi)存大小插入空閑內(nèi)存塊鏈表中。然后系統(tǒng)更新一下表示未分配內(nèi)存大小的變量 xFreeBytesRemaining。在釋放內(nèi)存完成之后的示意圖具體見圖 23-5 與圖 23-6。
      代碼清單 23-7(4):恢復(fù)調(diào)度器。

       

        從內(nèi)存的申請(qǐng)與釋放看來(lái), heap_2.c 方案采用的內(nèi)存管理算法雖然是高效但還是有缺陷的,由于在釋放內(nèi)存時(shí)不會(huì)將相鄰的內(nèi)存塊合并,所以這可能造成內(nèi)存碎片,當(dāng)然并不是說(shuō)這種內(nèi)存管理算法不好,只不過(guò)對(duì)使用的條件比較苛刻,要求用戶每次創(chuàng)建或釋放的任務(wù)、隊(duì)列等必須大小相同如果分配或釋放的內(nèi)存是隨機(jī)的,絕對(duì)不可以用這種內(nèi)存管理策略;如果申請(qǐng)和釋放的順序不可預(yù)料,那也很危險(xiǎn)。舉個(gè)例子, 假設(shè)用戶先申請(qǐng) 128 字節(jié)內(nèi)存,然后釋放,此時(shí)系統(tǒng)釋放的 128 字節(jié)內(nèi)存可以重復(fù)被利用; 如果用戶再接著申請(qǐng)64k 的字節(jié)內(nèi)存,那么一個(gè)本來(lái) 128 字節(jié)的大塊就會(huì)被分為兩個(gè) 64 字節(jié)的小塊,如果這種情況經(jīng)常發(fā)生,就會(huì)導(dǎo)致每個(gè)空閑塊都可能很小,最終在申請(qǐng)一個(gè)大塊時(shí)就會(huì)因?yàn)闆](méi)有合適的空閑內(nèi)存塊而申請(qǐng)失敗,這并不是因?yàn)榭偟目臻e內(nèi)存不足,而是無(wú)法申請(qǐng)到連續(xù)可以的大塊內(nèi)存。

        3.3 、heap_3.c

        heap_3.c 方案只是簡(jiǎn)單的封裝了標(biāo)準(zhǔn) C 庫(kù)中的 malloc()和 free()函數(shù), 并且能滿足常用的編譯器。 重新封裝后的 malloc()和 free()函數(shù)具有保護(hù)功能,采用的封裝方式是操作內(nèi)存前掛起調(diào)度器、完成后再恢復(fù)調(diào)度器。

        heap_3.c 方案具有以下特點(diǎn):

       ?。?)需要鏈接器設(shè)置一個(gè)堆, malloc()和 free()函數(shù)由編譯器提供。
       ?。?)具有不確定性。
       ?。?)很可能增大 RTOS 內(nèi)核的代碼大小。

        要 注 意 的 是 在 使 用 heap_3.c 方 案 時(shí) , FreeRTOSConfig.h 文 件 中 的configTOTAL_HEAP_SIZE 宏定義不起作用。 在 STM32 系列的工程中, 這個(gè)由編譯器定義的堆都在啟動(dòng)文件里面設(shè)置, 單位為字節(jié),我們具體以 STM32F10x 系列為例, 具體見下圖。而其它系列的都差不多。

        3.4、 heap_4.c

        heap_4.c 方案與 heap_2.c 方案一樣都采用最佳匹配算法來(lái)實(shí)現(xiàn)動(dòng)態(tài)的內(nèi)存分配,但是不一樣的是 heap_4.c 方案還包含了一種合并算法,能把相鄰的空閑的內(nèi)存塊合并成一個(gè)更大的塊,這樣可以減少內(nèi)存碎片。 heap_4.c 方案特別適用于移植層中可以直接使用pvPortMalloc()和 vPortFree()函數(shù)來(lái)分配和釋放內(nèi)存的代碼。

        內(nèi) 存 分 配 時(shí) 需 要 的 總 的 堆 空 間 由 文 件 FreeRTOSConfig.h 中 的 宏configTOTAL_HEAP_SIZE 配置,單位為字。 通過(guò)調(diào)用函數(shù) xPortGetFreeHeapSize() 我們可以知道還剩下多少內(nèi)存沒(méi)有使用, 但是并不包括內(nèi)存碎片。 這樣一來(lái)我們可以實(shí)時(shí)的調(diào)整和優(yōu)化 configTOTAL_HEAP_SIZE 的大小。

        heap_4.c 方案的空閑內(nèi)存塊也是以單鏈表的形式連接起來(lái)的, BlockLink_t 類型的局部靜態(tài)變量 xStart 表示鏈表頭,但 heap_4.c 內(nèi)存管理方案的鏈表尾部則保存在內(nèi)存堆空間最后位置,并使用 BlockLink_t 指針類型局部靜態(tài)變量 pxEnd 指向這個(gè)區(qū)域(而 heap_2.c 內(nèi)存管理方案則使用 BlockLink_t 類型的靜態(tài)變量 xEnd 表示鏈表尾)。

        heap_4.c 內(nèi)存管理方案的空閑塊鏈表不是以內(nèi)存塊大小進(jìn)行排序的,而是以內(nèi)存塊起始地址大小排序, 內(nèi)存地址小的在前,地址大的在后,因?yàn)?heap_4.c 方案還有一個(gè)內(nèi)存合并算法, 在釋放內(nèi)存的時(shí)候,假如相鄰的兩個(gè)空閑內(nèi)存塊在地址上是連續(xù)的,那么就可以合并為一個(gè)內(nèi)存塊, 這也是為了適應(yīng)合并算法而作的改變。

        heap_4.c 方案具有以下特點(diǎn):

        (1)可用于重復(fù)刪除任務(wù)、隊(duì)列、信號(hào)量、互斥量等的應(yīng)用程序
        (2)可用于分配和釋放隨機(jī)字節(jié)內(nèi)存的應(yīng)用程序, 但并不像 heap2.c 那樣產(chǎn)生嚴(yán)重的內(nèi)存碎片。
       ?。?)具有不確定性,但是效率比標(biāo)準(zhǔn) C 庫(kù)中的 malloc 函數(shù)高得多。

        3.4.1、內(nèi)存申請(qǐng)函數(shù) pvPortMalloc()

        heap_4.c 方案的內(nèi)存申請(qǐng)函數(shù)與 heap_2.c 方案的內(nèi)存申請(qǐng)函數(shù)大同小異,同樣是從鏈表頭 xStart 開始遍歷查找合適的內(nèi)存塊,如果某個(gè)空閑內(nèi)存塊的大小能容得下用戶要申請(qǐng)的內(nèi)存,則將這塊內(nèi)存取出用戶需要內(nèi)存空間大小的部分返回給用戶,剩下的內(nèi)存塊組成一個(gè)新的空閑塊,按照空閑內(nèi)存塊起始地址大小順序插入到空閑塊鏈表中,內(nèi)存地址小的在前,內(nèi)存地址大的在后。在插入到空閑內(nèi)存塊鏈表的過(guò)程中,系統(tǒng)還會(huì)執(zhí)行合并算法將地址相鄰的內(nèi)存塊進(jìn)行合并:判斷這個(gè)空閑內(nèi)存塊是相鄰的空閑內(nèi)存塊合并成一個(gè)大內(nèi)存塊,如果可以則合并,合并算法是 heap_4.c 內(nèi)存管理方案和 heap_2.c 內(nèi)存管理方案最大的不同之處,這樣一來(lái),會(huì)導(dǎo)致的內(nèi)存碎片就會(huì)大大減少,內(nèi)存管理方案適用性就很強(qiáng),能一樣隨機(jī)申請(qǐng)和釋放內(nèi)存的應(yīng)用中,靈活性得到大大的提高。

      /*******************************************************************************************************
        *@ 函數(shù)功能:申請(qǐng)內(nèi)存
        *@ 函數(shù)參數(shù):xWantedSize:申請(qǐng)內(nèi)存大小
        *@ 返回值:無(wú)
      *******************************************************************************************************/
      void *pvPortMalloc( size_t xWantedSize );

        3.4.2、內(nèi)存釋放函數(shù) vPortFree()

        heap_4.c 內(nèi)存管理方案的內(nèi)存釋放函數(shù) vPortFree()也比較簡(jiǎn)單, 根據(jù)傳入要釋放的內(nèi)存塊地址,偏移之后找到鏈表節(jié)點(diǎn),然后將這個(gè)內(nèi)存塊插入到空閑內(nèi)存塊鏈表中,在內(nèi)存塊插入過(guò)程中會(huì)執(zhí)行合并算法,這個(gè)我們已經(jīng)在內(nèi)存申請(qǐng)中講過(guò)了(而且合并算法多用于釋放內(nèi)存中) 。最后是將這個(gè)內(nèi)存塊標(biāo)志為“空閑” (內(nèi)存塊節(jié)點(diǎn)的 xBlockSize 成員變量最高位清 0)、再更新未分配的內(nèi)存堆大小即可。

      /*******************************************************************************************************
        *@ 函數(shù)功能:釋放內(nèi)存
        *@ 函數(shù)參數(shù):pv:指向要釋放的內(nèi)存
        *@ 返回值:無(wú)
      *******************************************************************************************************/
      void vPortFree( void *pv )

        3.5、heap_5.c

        heap_5.c 方案在實(shí)現(xiàn)動(dòng)態(tài)內(nèi)存分配時(shí)與 heap4.c 方案一樣, 采用最佳匹配算法和合并算法,并且允許內(nèi)存堆跨越多個(gè)非連續(xù)的內(nèi)存區(qū),也就是允許在不連續(xù)的內(nèi)存堆中實(shí)現(xiàn)內(nèi)存分配,比如用戶在片內(nèi) RAM 中定義一個(gè)內(nèi)存堆,還可以在外部 SDRAM 再定義一個(gè)或多個(gè)內(nèi)存堆,這些內(nèi)存都?xì)w系統(tǒng)管理。

        heap_5.c 方案通過(guò)調(diào)用 vPortDefineHeapRegions()函數(shù)來(lái)實(shí)現(xiàn)系統(tǒng)管理的內(nèi)存初始化,在內(nèi)存初始化未完成前不允許使用內(nèi)存分配和釋放函數(shù)。 如創(chuàng)建 FreeRTOS 對(duì)象(任務(wù)、隊(duì)列、信號(hào)量等)時(shí)會(huì)隱式的調(diào)用 pvPortMalloc()函數(shù),因此必須注意:使用 heap_5.c 內(nèi)存管理方案創(chuàng)建任何對(duì)象前,要先調(diào)用 vPortDefineHeapRegions()函數(shù)將內(nèi)存初始化。

        vPortDefineHeapRegions()函數(shù)只有一個(gè)形參, 該形參是一個(gè) HeapRegion_t 類型的結(jié)構(gòu)體數(shù)組。 HeapRegion_t 類型結(jié)構(gòu)體在 portable.h 中定義,具體見代碼清單。

      typedef struct HeapRegion 
      {
          /* 用于內(nèi)存堆的內(nèi)存塊起始地址*/
          uint8_t *pucStartAddress;
          /* 內(nèi)存塊大小 */
          size_t xSizeInBytes;
      } HeapRegion_t;

        用 戶 需 要 指 定 每 個(gè) 內(nèi) 存 堆 區(qū) 域 的 起 始 地 址 和 內(nèi) 存 堆 大 小 、 將 它 們 放 在 一 個(gè)HeapRegion_t 結(jié)構(gòu)體類型數(shù)組中, 這個(gè)數(shù)組必須用一個(gè) NULL 指針和 0 作為結(jié)尾,起始地址必須從小到大排列。假設(shè)我們?yōu)閮?nèi)存堆分配兩個(gè)內(nèi)存塊,第一個(gè)內(nèi)存塊大小為 0x10000字節(jié),起始地址為 0x80000000;第二個(gè)內(nèi)存塊大小為 0xa0000 字節(jié),起始地址為0x90000000, vPortDefineHeapRegions()函數(shù)使用實(shí)例具體見代碼清單。

      /* 在內(nèi)存中為內(nèi)存堆分配兩個(gè)內(nèi)存塊。
      第一個(gè)內(nèi)存塊大小為 0x10000 字節(jié),起始地址為 0x80000000,
      第二個(gè)內(nèi)存塊大小為 0xa0000 字節(jié),起始地址為 0x90000000。
      起始地址為 0x80000000 的內(nèi)存塊的起始地址更低,因此放到了數(shù)組的第一個(gè)位置。 */
      const HeapRegion_t xHeapRegions[] = {
          { ( uint8_t * ) 0x80000000UL, 0x10000 },
          { ( uint8_t * ) 0x90000000UL, 0xa0000 },
          { NULL, 0 } /* 數(shù)組結(jié)尾 */
      };
      
      /* 向函數(shù) vPortDefineHeapRegions()傳遞形參 */
      vPortDefineHeapRegions( xHeapRegions );

        用戶在自定義好內(nèi)存堆數(shù)組后,需要調(diào)用 vPortDefineHeapRegions()函數(shù)初始化這些內(nèi)存堆,系統(tǒng)會(huì)已一個(gè)空閑內(nèi)存塊鏈表的數(shù)據(jù)結(jié)構(gòu)記錄這些空閑內(nèi)存,鏈表以 xStart 節(jié)點(diǎn)構(gòu)開頭,以 pxEnd 指針指向的位置結(jié)束。 vPortDefineHeapRegions()函數(shù)對(duì)內(nèi)存的初始化與heap_4.c 方案一樣,在這里就不再重復(fù)贅述過(guò)程。以上面的內(nèi)存堆數(shù)組為例,初始化完成后的內(nèi)存堆示意圖具體見下圖。

       

      posted @ 2020-11-15 14:04  孤情劍客  閱讀(622)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 滨州市| 国产91午夜福利精品| 日韩一卡二卡三卡四卡五卡 | 日韩福利片午夜免费观着| 狠狠躁夜夜躁人人爽天天| 日韩高清不卡一区二区三区| 国产精品国产三级国快看| 天堂V亚洲国产V第一次| 国产精品自拍视频第一页| 中文字幕无码视频手机免费看| 国内精品久久久久影视| 日本妇人成熟免费| 国产精品激情av在线播放| 日韩乱码人妻无码中文字幕视频| 亚洲成人av在线系列| 国产91小视频在线观看| 亚洲精品理论电影在线观看| 久久综合色一综合色88| 熟女激情乱亚洲国产一区| 在线 欧美 中文 亚洲 精品| 成人无码午夜在线观看| 亚洲 中文 欧美 日韩 在线 | 国产不卡一区二区在线视频| 精品熟女少妇免费久久| 一日本道伊人久久综合影| 啊灬啊灬啊灬快灬高潮了电影片段| 国产乱妇乱子视频在播放| 在线观看中文字幕码国产| 疯狂做受xxxx高潮欧美日本| 天天爽天天摸天天碰| 国产一区二区三区麻豆视频| 国产精品综合av一区二区国产馆| 人妻系列中文字幕精品| 久久精品不卡一区二区| 成人亚欧欧美激情在线观看| 亚洲国产中文字幕精品| 国产成人无码A区在线观看视频| 国产亚欧女人天堂AV在线| 国产精品毛片一区视频播| 色欲精品国产一区二区三区av| 亚洲国产美国产综合一区|