天天看點

從結構體、記憶體池初始化到申請釋放,詳細解讀鴻蒙輕核心的動态記憶體管理

摘要:本文帶領大家一起剖析了鴻蒙輕核心的動态記憶體子產品的源代碼,包含動态記憶體的結構體、動态記憶體池初始化、動态記憶體申請、釋放等。

本文分享自華為雲社群《鴻蒙輕核心M核源碼分析系列九 動态記憶體Dynamic Memory》,原文作者:zhushy。

記憶體管理子產品管理系統的記憶體資源,它是作業系統的核心子產品之一,主要包括記憶體的初始化、配置設定以及釋放。

在系統運作過程中,記憶體管理子產品通過對記憶體的申請/釋放來管理使用者和OS對記憶體的使用,使記憶體的使用率和使用效率達到最優,同時最大限度地解決系統的記憶體碎片問題。

鴻蒙輕核心的記憶體管理分為靜态記憶體管理和動态記憶體管理,提供記憶體初始化、配置設定、釋放等功能。

動态記憶體:在動态記憶體池中配置設定使用者指定大小的記憶體塊。

優點:按需配置設定。

缺點:記憶體池中可能出現碎片。

靜态記憶體:在靜态記憶體池中配置設定使用者初始化時預設(固定)大小的記憶體塊。

優點:配置設定和釋放效率高,靜态記憶體池中無碎片。

缺點:隻能申請到初始化預設大小的記憶體塊,不能按需申請。

上一系列分析了靜态記憶體,我們開始分析動态記憶體。動态記憶體管理主要用于使用者需要使用大小不等的記憶體塊的場景。當使用者需要使用記憶體時,可以通過作業系統的動态記憶體申請函數索取指定大小的記憶體塊,一旦使用完畢,通過動态記憶體釋放函數歸還所占用記憶體,使之可以重複使用。

OpenHarmony LiteOS-M動态記憶體在TLSF算法的基礎上,對區間的劃分進行了優化,獲得更優的性能,降低了碎片率。動态記憶體核心算法框圖如下:

從結構體、記憶體池初始化到申請釋放,詳細解讀鴻蒙輕核心的動态記憶體管理

根據空閑記憶體塊的大小,使用多個空閑連結清單來管理。根據記憶體空閑塊大小分為兩個部分:[4, 127]和[27, 231],如上圖size class所示:

對[4,127]區間的記憶體進行等分,如上圖綠色部分所示,分為31個小區間,每個小區間對應記憶體塊大小為4位元組的倍數。每個小區間對應一個空閑記憶體連結清單和用于标記對應空閑記憶體連結清單是否為空的一個比特位,值為1時,空閑連結清單非空。[4,127]區間的記憶體使用1個32位無符号整數位圖示記。

大于127位元組的空閑記憶體塊,按照2的次幂區間大小進行空閑連結清單管理。總共分為24個小區間,每個小區間又等分為8個二級小區間,見上圖藍色的Size Class和Size SubClass部分。每個二級小區間對應一個空閑連結清單和用于标記對應空閑記憶體連結清單是否為空的一個比特位。總共24*8=192個二級小區間,對應192個空閑連結清單和192/32=6個32位無符号整數位圖示記。

例如,當有40位元組的空閑記憶體需要插入空閑連結清單時,對應小區間[40,43],第10個空閑連結清單,位圖示記的第10比特位。把40位元組的空閑記憶體挂載第10個空閑連結清單上,并判斷是否需要更新位圖示記。當需要申請40位元組的記憶體時,根據位圖示記擷取存在滿足申請大小的記憶體塊的空閑連結清單,從空閑連結清單上擷取空閑記憶體節點。如果配置設定的節點大于需要申請的記憶體大小,進行分割節點操作,剩餘的節點重新挂載到相應的空閑連結清單上。當有580位元組的空閑記憶體需要插入空閑連結清單時,對應二級小區間[2^9,2^9+2^6],第31+2*8=47個空閑連結清單,第2個位圖示記的第17比特位。把580位元組的空閑記憶體挂載第47個空閑連結清單上,并判斷是否需要更新位圖示記。當需要申請580位元組的記憶體時,根據位圖示記擷取存在滿足申請大小的記憶體塊的空閑連結清單,從空閑連結清單上擷取空閑記憶體節點。如果配置設定的節點大于需要申請的記憶體大小,進行分割節點操作,剩餘的節點重新挂載到相應的空閑連結清單上。如果對應的空閑連結清單為空,則向更大的記憶體區間去查詢是否有滿足條件的空閑連結清單,實際計算時,會一次性查找到滿足申請大小的空閑連結清單。

動态記憶體管理結構如下圖所示:

從結構體、記憶體池初始化到申請釋放,詳細解讀鴻蒙輕核心的動态記憶體管理

記憶體池池頭部分

記憶體池池頭部分包含記憶體池資訊和位圖示記數組和空閑連結清單數組。記憶體池資訊包含記憶體池起始位址及堆區域總大小,記憶體池屬性。位圖示記數組有7個32位無符号整數組成,每個比特位标記對應的空閑連結清單是否挂載空閑記憶體塊節點。空閑記憶體連結清單包含223個空閑記憶體頭節點資訊,每個空閑記憶體頭節點資訊維護記憶體節點頭和空閑連結清單中的前驅、後繼空閑記憶體節點。

記憶體池節點部分

包含3種類型節點,未使用空閑記憶體節點,已使用記憶體節點,尾節點。每個記憶體節點維護一個前序指針,指向記憶體池中上一個記憶體節點,維護大小和使用标記,标記該記憶體節點的大小和是否使用等。空閑記憶體節點和已使用記憶體節點後面的資料域,尾節點沒有資料域。

本文通過分析動态記憶體子產品的源碼,幫助讀者掌握動态記憶體的使用。本文中所涉及的源碼,以OpenHarmony LiteOS-M核心為例,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 擷取。接下來,我們看下動态記憶體的結構體,動态記憶體初始化,動态記憶體常用操作的源代碼。

動态記憶體的結構體有動态記憶體池資訊結構體OsMemPoolInfo,動态記憶體池頭結構體OsMemPoolHead、動态記憶體節點頭結構體OsMemNodeHead,已使用記憶體節點結構體OsMemUsedNodeHead,空閑記憶體節點結構體OsMemFreeNodeHead。這些結構體定義在檔案kernel\src\mm\los_memory.c中,下文會結合上文的動态記憶體管理結構示意圖對各個結構體的成員變量進行說明。

動态記憶體池資訊結構體OsMemPoolInfo維護記憶體池的開始位址和大小資訊。三個主要的成員是記憶體池開始位址.pool,記憶體池大小.poolSize和記憶體值屬性.attr。如果開啟宏LOSCFG_MEM_WATERLINE,還會維護記憶體池的水線數值。

動态記憶體池頭結構體OsMemPoolHead源碼如下,除了動态記憶體池資訊結構體struct OsMemPoolInfo info,還維護2個數組,一個是空閑記憶體連結清單位圖數組freeListBitmap[],一個是空閑記憶體連結清單數組freeList[]。宏定義OS_MEM_BITMAP_WORDS和OS_MEM_FREE_LIST_COUNT後文會介紹。

先看下動态記憶體節點頭結構體OsMemNodeHead的定義,⑴處如果開啟記憶體節點完整性檢查的宏LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK,會維護魔術字.magic進行校驗。⑵處如果開啟記憶體洩漏檢查的宏,會維護連結寄存器數組linkReg[]。⑶處的成員變量是個指針組合體,記憶體池中的每個記憶體節點頭維護指針執行上一個記憶體節點。⑷處維護記憶體節點的大小和标記資訊。

接着看下已使用記憶體節點結構體OsMemUsedNodeHead,該結構體比較簡單,直接以動态記憶體節點頭結構體OsMemNodeHead作為唯一的成員。

我們再看下空閑記憶體節點結構體OsMemFreeNodeHead,除了動态記憶體節點頭結構體OsMemNodeHead成員,還包含2個指針分别指向上一個和下一個空閑記憶體節點。

動态記憶體中還提供了一些和TLSF算法相關的宏定義和内聯函數,這些宏非常重要,在分析源代碼前需要熟悉下這些宏的定義。可以結合上文的動态記憶體核心算法框圖進行學習。⑴處的宏對處于[2^n,2^(n+1)],其中(n=7,8,…30)區間的大記憶體塊進行2^3=8等分。⑵處的宏,定義處于[4,127]區間的小記憶體塊劃分為31個,即4,8,12,…,124。⑶處定義小記憶體的上界值,考慮記憶體對齊和粒度,最大值隻能取到124。

⑷處的宏表示處于[2^n,2^(n+1)],其中(n=7,8,…30)區間的大記憶體分為24個小區間,其中n=7 就是⑺處定義的宏OS_MEM_LARGE_START_BUCKET。⑻處對應空閑記憶體連結清單的長度。⑼處是空閑連結清單位圖數組的長度,31個小記憶體使用1個位圖字,是以需要加1。⑽處定義位圖掩碼,每個位圖字是32位無符号整數。

繼續看下内聯函數。⑾處函數查找位圖字中的第一個1的比特位,這個實作的功能類似内建函數__builtin_ctz。該函數用于擷取空閑記憶體連結清單對應的位圖字中,第一個挂載着空閑記憶體塊的空閑記憶體連結清單。⑿處擷取位圖字中的最後一個1的比特位,(從32位二進制數值從左到右依次第0,1,…,31位)。⒀處函數名稱中的Log是對數英文logarithm的縮寫,函數用于計算以2為底的對數的整數部分。⒁處擷取記憶體區間的大小級别編号,對于小于128位元組的,有31個級别,對處于[2^n,2^(n+1)],其中(n=7,8,…30)區間的記憶體,有24個級别。⒂處根據記憶體大小,記憶體區間一級編号擷取擷取二級小區間的編号,對處于[2^n,2^(n+1)],其中(n=7,8,…30)區間的記憶體,有8個二級小區間。

動态記憶體管理子產品為使用者提供初始化和删除記憶體池、申請、釋放動态記憶體等操作,我們來分析下接口的源代碼。在分析下記憶體操作接口之前,我們先看下一下常用的内部接口。

⑴處函數OsMemSetFreeListBit需要2個參數,一個是記憶體池池頭head,一個是空閑記憶體連結清單索引index。當空閑記憶體連結清單上挂載有空閑記憶體塊時,位圖字相應的位需要設定為1。⑴處函數OsMemClearFreeListBit做相反的操作,當空閑記憶體連結清單上不再挂載空閑記憶體塊時,需要對應的比特位清零。

函數VOID OsMemMergeNode(struct OsMemNodeHead *node)用于合并給定節點struct OsMemNodeHead *node和它前一個空閑節點。⑴處把前一個節點的大小加上要合入節點的大小。⑵處擷取給定節點的下一個節點,然後執行⑶把它的前一個節點指向給定節點的前一個節點,完成節點的合并。其中宏OS_MEM_NODE_GET_LAST_FLAG用于判斷是否最後一個節點,預設為0,可以自行檢視下該宏的定義。

函數VOID OsMemSplitNode(VOID *pool, struct OsMemNodeHead *allocNode, UINT32 allocSize)用于分割記憶體節點,需要三個參數。VOID *pool是記憶體池起始位址,struct OsMemNodeHead *allocNode表示從該記憶體節點配置設定出需要的記憶體,UINT32 allocSize是需要配置設定的記憶體大小。分割之後剩餘的部分,如果下一個節點是空閑節點,則合并一起。分割剩餘的節點會挂載到空閑記憶體連結清單上。

⑴處表示newFreeNode是配置設定之後剩餘的空閑記憶體節點,設定它的上一個節點為配置設定的節點,并設定剩餘記憶體大小。⑵處調整配置設定記憶體的大小,⑶處擷取下一個節點,然後執行⑷下一個節點的前一個節點設定為新的空閑節點newFreeNode。⑸處判斷下一個節點是否被使用,如果沒有使用,則把下一個節點從連結清單中删除,然後和空閑節點newFreeNode合并。⑹處分割剩餘的空閑記憶體節點挂載到連結清單上。

OsMemReAllocSmaller()函數用于從一個大的記憶體塊裡重新申請一個較小的記憶體,他需要的4個參數分别是:VOID *pool是記憶體池起始位址,UINT32 allocSize是重新申請的記憶體的大小,struct OsMemNodeHead *node是目前需要重新配置設定記憶體的記憶體節點,UINT32 nodeSize是目前節點的大小。⑴設定記憶體節點selfNode.sizeAndFlag為去除标記後的實際大小,⑵按需分割節點,⑶分割後的節點設定已使用标記,完成完成申請記憶體。

最後,再來看下函數函數OsMemMergeNodeForReAllocBigger(),用于合并記憶體節點,重新配置設定更大的記憶體空間。它需要5個參數,VOID *pool是記憶體池起始位址,UINT32 allocSize是重新申請的記憶體的大小,struct OsMemNodeHead *node是目前需要重新配置設定記憶體的記憶體節點,UINT32 nodeSize是目前節點的大小,struct OsMemNodeHead *nextNode是下一個記憶體節點。⑴處設定記憶體節點的大小為去除标記後的實際大小,⑵把下一個節點從連結清單上删除,然後合并節點。⑶處如果合并後的節點大小超過需要重新配置設定的大小,則分割節點。⑷處把申請的記憶體節點标記為已使用,完成完成申請記憶體

動态記憶體提供了針對空閑記憶體連結清單的幾個操作,我們依次分析下這些操作的代碼。首先看下函數OsMemFreeListIndexGet,根據記憶體節點大小擷取空閑記憶體連結清單的索引。⑴處先擷取一級索引,⑵處擷取二級索引,然後計算空閑連結清單的索引并傳回。

接着看下函數OsMemListAdd,如何把空閑記憶體節點插入空閑記憶體連結清單。⑴處擷取空閑連結清單的第一個節點,如果節點不為空,則把這個節點的前驅節點設定為待插入節點node。⑵處設定待插入節點的前驅、後繼節點,然後把該節點指派給空閑連結清單pool->freeList[listIndex]。最後執行⑶處代碼,把設定空閑連結清單位圖字,并設定魔術字。

最後,分析下函數OsMemListDelete如何從空閑記憶體連結清單删除指定的空閑記憶體節點。⑴處如果删除的節點是空閑記憶體連結清單的第一個節點,則需要把空閑連結清單執行待删除節點的下一個節點。如果下一個節點為空,需要執行⑵清除空閑連結清單的位圖字。否則執行⑶把下一個節點的前驅節點設定為空。如果待删除節點不是空閑連結清單的第一個節點,執行⑷把待删除節點的前驅節點的後續節點設定為待删除節點的後繼節點。如果待删除節點不為最後一個節點,需要執行⑸把待删除節點的後繼節點的前驅節點設定為待删除節點的前驅節點。最後需要設定下魔術字。

動态記憶體提供了針對空閑記憶體的幾個操作,如OsMemFreeNodeAdd、OsMemFreeNodeDelete、OsMemFreeNodeGet。

函數OsMemFreeNodeAdd用于把一個空閑記憶體節點加入相應的空閑記憶體連結清單上。⑴處調用函數擷取空閑記憶體連結清單的索引,然後執行⑵把空閑記憶體節點加入空閑連結清單。

函數OsMemFreeNodeDelete用于把一個空閑記憶體節點從相應的空閑記憶體連結清單上删除。代碼較簡單,擷取空閑記憶體連結清單的索引,然後調用函數OsMemListDelete進行删除。

函數OsMemFreeNodeGet根據記憶體池位址和需要的記憶體大小擷取滿足大小條件的空閑記憶體塊。⑴處調用函數擷取滿足大小條件的記憶體塊,然後執行⑵把擷取到的記憶體塊從空閑記憶體連結清單删除,傳回記憶體節點位址。

最後,分析下函數OsMemFindNextSuitableBlock。⑴處根據需要的記憶體塊大小擷取一級區間編号,如果申請的記憶體處于[4,127]區間,執行⑵處記錄空閑記憶體連結清單索引。如果需要申請的是大記憶體,執行⑶處代碼。先擷取二級區間索引,然後計算出空閑記憶體連結清單的索引值index。這樣計算出來的空閑記憶體連結清單下可能并沒有挂載空閑記憶體塊,調用⑷處函數OsMemNotEmptyIndexGet擷取挂載空閑記憶體塊的空閑記憶體連結清單索引值。如果成功擷取到滿足大小的空閑記憶體塊,傳回空閑連結清單索引值,否則繼續執行後續代碼。⑹處對空閑連結清單位圖字進行周遊,循環中的自增變量index對應一級區間編号。如果位圖字不為空,執行⑺擷取這個位圖字對應的最大的空閑記憶體連結清單的索引。

如果執行到⑻處,說明沒有比對到合适的記憶體塊,傳回空指針。⑼處表示存在滿足大小的空閑記憶體連結清單,調用函數OsMemFindCurSuitableBlock擷取合适的記憶體塊并傳回。⑽處标簽表示擷取到合适的空閑記憶體連結清單索引,傳回空閑記憶體連結清單。

我們再詳細分析下函數OsMemNotEmptyIndexGet的源碼。⑴處根據空閑記憶體連結清單索引擷取位圖字,⑵處判斷空閑記憶體連結清單索引對應的一級記憶體區間對應的二級小記憶體區間是否存在滿足條件的空閑記憶體塊。其中index & OS_MEM_BITMAP_MASK對索引隻取低5位後,可以把索引值和位圖字中的比特位關聯起來,比如index為39時,index & OS_MEM_BITMAP_MASK等于7,對應位圖字的第7位。表達式~((1 << (index & OS_MEM_BITMAP_MASK)) - 1)則用于表示大于空閑記憶體連結清單索引index的索引值對應的位圖字。⑵處的語句執行後,mask就表示空閑連結清單索引值大于index的連結清單索引對應的位圖字的值。當mask不為0時,表示存在滿足記憶體大小的空閑記憶體塊,則執行⑶處代碼,其中OsMemFFS(mask)擷取位圖字中第一個為1的比特位位數,該位對應着挂載空閑記憶體塊的連結清單。(index & ~OS_MEM_BITMAP_MASK)對應連結清單索引的高位,加上位圖字位數就計算出挂載着滿足申請條件的空閑記憶體連結清單的索引值。

最後,再看下函數OsMemFindCurSuitableBlock。⑴處循環周遊空閑記憶體連結清單上挂載的記憶體塊,如果周遊到的記憶體塊大小大于需要的大小,則執行⑵傳回該空閑記憶體塊。否則傳回空指針。

我們分析下初始化動态記憶體池函數UINT32 LOS_MemInit(VOID *pool, UINT32 size)的代碼。我們先看看函數參數,VOID *pool是動态記憶體池的起始位址,UINT32 size是初始化的動态記憶體池的總大小,size需要小于等于*pool開始的記憶體區域的大小,否則會影響後面的記憶體區域,還需要大于動态記憶體池的最小值OS_MEM_MIN_POOL_SIZE。[pool, pool + size]不能和其他記憶體池沖突。

我們看下代碼,⑴處對傳入參數進行校驗,⑵處對傳入參數進行是否記憶體對齊校驗,如果沒有記憶體對齊會傳回錯誤碼。⑶處調用函數OsMemPoolInit()進行記憶體池初始化,這是初始化記憶體的核心函數。⑷處開啟宏LOSCFG_MEM_MUL_POOL多記憶體池支援時,才會執行。

我們繼續看下函數OsMemPoolInit()。⑴處設定動态記憶體池資訊結構體struct OsMemPoolHead *poolHead的起始位址和大小,⑵處設定記憶體池屬性設定為鎖定、不可擴充。⑶處擷取記憶體池的第一個記憶體控制節點,然後設定它的大小,該節點大小等于記憶體池總大小減去記憶體池池頭大小和一個記憶體節點頭大小。然後再設定該記憶體節點的上一個節點為記憶體池的最後一個節點OS_MEM_END_NODE(pool, size)。

⑷處調用宏給節點設定魔術字,然後把記憶體節點插入到空閑記憶體連結清單中。⑸處擷取記憶體池的尾節點,設定魔術字,然後執行⑹設定尾節點大小為0和設定上一個節點,并設定已使用标記。如果開啟調測宏LOSCFG_MEM_WATERLINE,還會有些其他操作,自行閱讀即可。

初始化動态記憶體池後,我們可以使用函數VOID *LOS_MemAlloc(VOID *pool, UINT32 size)來申請動态記憶體,下面分析下源碼。

⑴處對參數進行校驗,記憶體池位址不能為空,申請的記憶體大小不能為0。⑵處判斷申請的記憶體大小是否已标記為使用或記憶體對齊。⑶處調用函數OsMemAlloc(poolHead, size, intSave)申請記憶體塊。

我們繼續分析函數OsMemAlloc()。⑴處對申請記憶體大小加上頭結點大小的和進行記憶體對齊,⑵處從空閑記憶體連結清單中擷取一個滿足申請大小的空閑記憶體塊,如果申請失敗,則列印錯誤資訊。⑶處如果找到的記憶體塊大于需要的記憶體大小,則執行分割操作。⑷處把已配置設定的記憶體節點标記為已使用,更新水線記錄。⑸傳回記憶體塊的資料區的位址,這個是通過記憶體節點位址加1定位到資料區記憶體位址實作的。申請記憶體完成,調用申請記憶體的函數中可以使用申請的記憶體了。

我們還可以使用函數VOID *LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary),從指定動态記憶體池中申請長度為size且位址按boundary位元組對齊的記憶體。該函數需要3個參數,VOID *pool為記憶體池起始位址,UINT32 size為需要申請的記憶體大小,UINT32 boundary記憶體對齊數值。當申請記憶體後得到的記憶體位址VOID *ptr,對齊後的記憶體位址為VOID *alignedPtr,二者的偏移值使用UINT32 gapSize儲存。因為已經按OS_MEM_ALIGN_SIZE記憶體對齊了,最大偏移值為boundary - OS_MEM_ALIGN_SIZE。下面分析下源碼。

⑴處對參數進行校驗,記憶體池位址不能為空,申請的記憶體大小不能為0,對齊位元組boundary不能為0,還需要是2的幂。申請的記憶體大小必須大于最小的申請值OS_MEM_MIN_ALLOC_SIZE。⑵處校驗下對齊記憶體後是否會資料溢出。⑶處計算對齊後需要申請的記憶體大小,然後判斷記憶體大小數值沒有已使用或已對齊标記。⑷處調用函數申請到記憶體VOID *ptr,然後計算出對齊的記憶體位址VOID *alignedPtr,如果二者相等則傳回。⑸處計算出對齊記憶體的偏移值,⑹處擷取申請到的記憶體的頭節點,設定已對齊标記。⑺對偏移值設定對齊标記,然後把偏移值儲存在記憶體VOID *alignedPtr的前4個位元組裡。⑻處重新定向要傳回的指針,完成申請對齊的記憶體。

對申請的記憶體塊使用完畢,我們可以使用函數UINT32 LOS_MemFree(VOID *pool, VOID *ptr)來釋放動态态記憶體,需要2個參數,VOID *pool是初始化過的動态記憶體池位址。VOID *ptr是需要釋放的動态記憶體塊的資料區的起始位址,注意這個不是記憶體控制節點的位址。下面分析下源碼,⑴處對傳入的參數先進行校驗。⑵處擷取校準記憶體對齊後的真實的記憶體位址,然後擷取記憶體節點頭位址。⑶處調用函數OsMemFree(pool, ptr)完成記憶體的釋放。

我們回過頭來,繼續看下函數OsGetRealPtr()。⑴擷取記憶體對齊的偏移值,⑵如果偏移值同時标記為已使用和已對齊,則傳回錯誤。⑶如果偏移值标記為已對齊,則執行⑷去除對齊标記,擷取不帶标記的偏移值。然後執行⑸,擷取記憶體對齊之前的資料區記憶體位址。

我們還可以使用函數VOID *LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size),按指定size大小重新配置設定記憶體塊,并将原記憶體塊内容拷貝到新記憶體塊。如果新記憶體塊申請成功,則釋放原記憶體塊。該函數需要3個參數,VOID *pool為記憶體池起始位址,VOID *ptr為之前申請的記憶體位址,UINT32 size為重新申請的記憶體大小。傳回值為新記憶體塊位址,或者傳回NULL。下面分析下源碼。

⑴處對參數進行校驗,記憶體池位址不能為空,記憶體大小不能含有已使用、已對齊标記。⑵處如果傳入的記憶體位址為空,則等價于LOS_MemAlloc()函數。⑶如果傳入size為0,等價于函數LOS_MemFree()。⑷處保證申請的記憶體塊大小至少為系統允許的最小值OS_MEM_MIN_ALLOC_SIZE。⑸處擷取記憶體對齊之前的記憶體位址,上文已分析該函數OsGetRealPtr()。⑹處由資料域記憶體位址計算出記憶體控制節點node的記憶體位址,然後執行⑺處函數重新申請記憶體。

繼續分析下函數OsMemRealloc。⑴處處理重新申請的記憶體小于等于現有的記憶體的情況,需要調用函數OsMemReAllocSmaller()進行分割,分割完畢傳回(VOID *)ptr即可。如果重新申請更大的記憶體,則執行⑵處代碼擷取下一個節點,然後執行⑶處理下一個節點可用且兩個節點大小之和大于等于重新申請記憶體的大小allocSize。執行⑷處的函數,合并節點重新配置設定記憶體。

如果連續的節點的大小不滿足重新申請記憶體的大小,則執行⑸處函數重新申請記憶體。申請成功後,執行⑹把之前記憶體的資料複制到新申請的記憶體區域,複制失敗的話,則把新申請的記憶體釋放掉,并傳回NULL,退出函數。如果複制成功,繼續執行⑺釋放掉之前的節點。

本文帶領大家一起剖析了鴻蒙輕核心的靜态記憶體子產品的源代碼,包含動态記憶體的結構體、動态記憶體池初始化、動态記憶體申請、釋放等。感謝閱讀,如有任何問題、建議,都可以留言給我們: https://gitee.com/openharmony/kernel_liteos_m/issues 。為了更容易找到鴻蒙輕核心代碼倉,建議通路 https://gitee.com/openharmony/kernel_liteos_m ,關注<code>Watch</code>、點贊<code>Star</code>、并<code>Fork</code>到自己賬戶下,謝謝。

點選關注,第一時間了解華為雲新鮮技術~

下一篇: RegExp

繼續閱讀