LiteOS內(nèi)核源碼分析系列十四 動態(tài)內(nèi)存Bestfit_little分配算法

      網(wǎng)友投稿 794 2025-04-02

      LiteOS內(nèi)核源碼分析系列十四 動態(tài)內(nèi)存Bestfit_little分配算法


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

      在系統(tǒng)運行過程中,內(nèi)存管理模塊通過對內(nèi)存的申請/釋放來管理用戶和OS對內(nèi)存的使用,使內(nèi)存的利用率和使用效率達到最優(yōu),同時最大限度地解決系統(tǒng)的內(nèi)存碎片問題。

      Huawei LiteOS的內(nèi)存管理分為靜態(tài)內(nèi)存管理和動態(tài)內(nèi)存管理,提供內(nèi)存初始化、分配、釋放等功能。

      動態(tài)內(nèi)存:在動態(tài)內(nèi)存池中分配用戶指定大小的內(nèi)存塊。

      優(yōu)點:按需分配。

      缺點:內(nèi)存池中可能出現(xiàn)碎片。

      靜態(tài)內(nèi)存:在靜態(tài)內(nèi)存池中分配用戶初始化時預(yù)設(shè)(固定)大小的內(nèi)存塊。

      優(yōu)點:分配和釋放效率高,靜態(tài)內(nèi)存池中無碎片。

      缺點:只能申請到初始化預(yù)設(shè)大小的內(nèi)存塊,不能按需申請。

      上一系列分析了靜態(tài)內(nèi)存,我們開始分析動態(tài)內(nèi)存。動態(tài)內(nèi)存管理主要用于用戶需要使用大小不等的內(nèi)存塊的場景。當(dāng)用戶需要使用內(nèi)存時,可以通過操作系統(tǒng)的動態(tài)內(nèi)存申請函數(shù)索取指定大小的內(nèi)存塊,一旦使用完畢,通過動態(tài)內(nèi)存釋放函數(shù)歸還所占用內(nèi)存,使之可以重復(fù)使用。

      LiteOS動態(tài)內(nèi)存支持bestfit(也稱為dlink)和bestfit_little兩種內(nèi)存管理算法。上一系系列已經(jīng)分析過動態(tài)內(nèi)存的bestfit的算法,本文繼續(xù)分析LiteOS動態(tài)內(nèi)存的bestfit_little算法。

      本文通過分析LiteOS動態(tài)內(nèi)存模塊的源碼,幫助讀者掌握動態(tài)內(nèi)存的使用。LiteOS動態(tài)內(nèi)存模塊的源代碼,均可以在LiteOS開源站點https://gitee.com/LiteOS/LiteOS 獲取。動態(tài)內(nèi)存bestfit_little算法的源代碼、開發(fā)文檔,示例程序代碼如下:

      LiteOS內(nèi)核動態(tài)內(nèi)存源代碼

      包括動態(tài)內(nèi)存的bestfit_little算法私有頭文件kernel\base\mem\bestfit_little\los_memory_internal.h、動態(tài)內(nèi)存私有頭文件kernel\base\include\los_memory_pri.h、內(nèi)存頭文件kernel\include\los_memory.h、C源代碼文件kernel\base\mem\bestfit_little\los_memory.c、C源代碼文件kernel\base\mem\bestfit_little\los_heap.c。

      開發(fā)指南文檔–內(nèi)存

      在線文檔https://gitee.com/LiteOS/LiteOS/blob/master/doc/LiteOS_Kernel_Developer_Guide.md#%E5%86%85%E5%AD%98。

      接下來,我們看下動態(tài)內(nèi)存的結(jié)構(gòu)體,動態(tài)內(nèi)存初始化,動態(tài)內(nèi)存常用操作的源代碼。

      1、動態(tài)內(nèi)存結(jié)構(gòu)體定義和常用宏定義

      1.1 動態(tài)內(nèi)存結(jié)構(gòu)體定義

      動態(tài)內(nèi)存bestfit_little算法的結(jié)構(gòu)體有動態(tài)內(nèi)存池信息結(jié)構(gòu)體LosMemPoolInfo即內(nèi)存管理節(jié)點struct LosHeapManager,動態(tài)內(nèi)存節(jié)點結(jié)構(gòu)體LosHeapNode。本系列先不考慮對SLAB內(nèi)存的支持。

      1.1.1 動態(tài)內(nèi)存池信息結(jié)構(gòu)體LosMemPoolInfo

      在文件kernel\base\include\los_memory_pri.h中,定義了內(nèi)存池信息結(jié)構(gòu)體LosMemPoolInfo。動態(tài)內(nèi)存bestfit算法和bestfit_little算法中都定義了該結(jié)構(gòu)體,結(jié)構(gòu)體名稱一樣,成員有差異,我們本系列看看bestfit_little算法的結(jié)構(gòu)體,源代碼如下。三個主要的成員是內(nèi)存池頭節(jié)點struct LosHeapNode *head、內(nèi)存池尾節(jié)點struct LosHeapNode *tail和內(nèi)存池大小UINT32 size。其他結(jié)構(gòu)體需要開啟相應(yīng)的宏才生效,暫不討論這些宏相關(guān)的特性。

      typedef struct LosHeapManager { struct LosHeapNode *head; struct LosHeapNode *tail; UINT32 size; #ifdef LOSCFG_MEM_TASK_STAT Memstat stat; #endif #ifdef LOSCFG_MEM_MUL_POOL VOID *nextPool; #endif #ifdef LOSCFG_KERNEL_MEM_SLAB_EXTENTION struct LosSlabControlHeader slabCtrlHdr; #endif } LosMemPoolInfo;

      1.1.2 動態(tài)內(nèi)存池節(jié)點結(jié)構(gòu)體LosHeapNode

      在文件kernel\base\mem\bestfit_little\los_memory_internal.h中,定義了動態(tài)內(nèi)存池節(jié)點結(jié)構(gòu)體LosHeapNode。結(jié)構(gòu)體源代碼如下,非常簡單,成員包含指向上一個節(jié)點的指針struct LosHeapNode *prev,節(jié)點的大小size,是否使用標(biāo)記used,是否對齊標(biāo)記align,數(shù)據(jù)區(qū)地址UINT8 data[0]。

      LiteOS內(nèi)核源碼分析系列十四 動態(tài)內(nèi)存Bestfit_little分配算法

      struct LosHeapNode { struct LosHeapNode *prev; #ifdef LOSCFG_MEM_TASK_STAT UINT32 taskId; #endif UINT32 size : 30; UINT32 used : 1; UINT32 align : 1; UINT8 data[0]; };

      1.2 動態(tài)內(nèi)存常用宏定義

      我們先看看使用到的宏定義,這些宏非常重要,在分析源代碼前需要熟悉下這些宏的定義。在文件kernel\base\mem\bestfit_little\los_heap.c中定義了些和heap操作相關(guān)的宏。HEAP_CAST(t, exp)用于指針類型轉(zhuǎn)換,HEAP_ALIGN表示內(nèi)存對齊大小,MALLOC_MAXSIZE表示允許申請的最大內(nèi)存。

      #define HEAP_CAST(t, exp) ((t)(exp)) #define HEAP_ALIGN 4 #define MALLOC_MAXSIZE (0xFFFFFFFF - HEAP_ALIGN + 1)

      動態(tài)內(nèi)存頭文件kernel\base\mem\bestfit\los_memory_internal.h中也提供了一些重要的宏定義。

      ⑴處定義兩個宏用于內(nèi)存對齊,⑵處定義是否對齊標(biāo)記位,即高31位。然后分別定義3個宏函數(shù),OS_MEM_SET_ALIGN_FLAG()設(shè)置標(biāo)記為對齊,OS_MEM_GET_ALIGN_FLAG()獲取是否已對齊,OS_MEM_GET_ALIGN_GAPSIZE()獲取去除標(biāo)記后的使用大小。

      ⑴ #define ALIGNE(sz) (((sz) + HEAP_ALIGN - 1) & (~(HEAP_ALIGN - 1))) #define OS_MEM_ALIGN(value, align) (((UINT32)(UINTPTR)(value) + (UINT32)((align) - 1)) & \ (~(UINT32)((align) - 1))) ⑵ #define OS_MEM_ALIGN_FLAG 0x80000000 #define OS_MEM_SET_ALIGN_FLAG(align) ((align) = ((align) | OS_MEM_ALIGN_FLAG)) #define OS_MEM_GET_ALIGN_FLAG(align) ((align) & OS_MEM_ALIGN_FLAG) #define OS_MEM_GET_ALIGN_GAPSIZE(align) ((align) & (~OS_MEM_ALIGN_FLAG))

      2、動態(tài)內(nèi)存常用操作

      Huawei LiteOS系統(tǒng)中的動態(tài)內(nèi)存管理模塊為用戶提供初始化和刪除內(nèi)存池、申請、釋放動態(tài)內(nèi)存等操作,我們來分析下接口的源代碼。在分析下內(nèi)存操作接口之前,我們先看下一下常用的內(nèi)部heap接口。

      2.1 動態(tài)內(nèi)存內(nèi)部Heap接口

      在文件kernel\base\mem\bestfit_little\los_heap.c中定義了內(nèi)存操作相關(guān)的接口,如初始化內(nèi)存池、申請、釋放動態(tài)內(nèi)存等。

      2.1.1 堆內(nèi)存初始化OsHeapInit

      函數(shù)BOOL OsHeapInit(VOID *pool, UINT32 size)用于初始化堆內(nèi)存,需要2個參數(shù):VOID *pool是內(nèi)存池的起始地址,UINT32 size為內(nèi)存池的大小。⑴處轉(zhuǎn)換內(nèi)存池起始地址為內(nèi)存管理節(jié)點,⑵進行參數(shù)校驗,如果內(nèi)存池地址為空,內(nèi)存池大小小于內(nèi)存管理節(jié)點和一個內(nèi)存節(jié)點的大小之和,則返回初始化失敗。⑶清除內(nèi)存池的內(nèi)容,⑷設(shè)置內(nèi)存管理節(jié)點的size成員變量數(shù)據(jù)為內(nèi)存節(jié)點區(qū)的大小。⑸處獲取第一個內(nèi)存節(jié)點的地址,該節(jié)點為內(nèi)存管理節(jié)點的頭結(jié)點,也是尾節(jié)點。⑹設(shè)置內(nèi)存節(jié)點相關(guān)信息,大小、前一個節(jié)點指針,內(nèi)存節(jié)點的數(shù)據(jù)區(qū)大小。⑺處內(nèi)存使用統(tǒng)計暫不分析,自行查看即可。

      BOOL OsHeapInit(VOID *pool, UINT32 size) { struct LosHeapNode *node = NULL; ⑴ struct LosHeapManager *heapMan = HEAP_CAST(struct LosHeapManager *, pool); ⑵ if ((heapMan == NULL) || (size <= (sizeof(struct LosHeapNode) + sizeof(struct LosHeapManager)))) { return FALSE; } ⑶ (VOID)memset_s(pool, size, 0, size); ⑷ heapMan->size = size - sizeof(struct LosHeapManager); ⑸ node = heapMan->head = (struct LosHeapNode *)((UINT8*)pool + sizeof(struct LosHeapManager)); heapMan->tail = node; ⑹ node->used = 0; node->prev = NULL; node->size = heapMan->size - sizeof(struct LosHeapNode); ⑺ OsHeapStatInit(heapMan, size); return TRUE; }

      2.1.2 申請內(nèi)存OsHeapAlloc

      函數(shù)VOID *OsHeapAlloc(VOID *pool, UINT32 size)用于申請內(nèi)存塊,VOID *pool為內(nèi)存池起始地址,UINT32 size為要申請的內(nèi)存大小。⑴處計算內(nèi)存對齊后的大小,⑵轉(zhuǎn)換為內(nèi)存管理節(jié)點,然后進行參數(shù)校驗,確保內(nèi)存池地址不為空,申請的內(nèi)存大小不超標(biāo)。⑶處獲取內(nèi)存尾節(jié)點,當(dāng)不為空時,循環(huán)遍歷各個內(nèi)存節(jié)點,尋找出最佳的滿足申請大小的內(nèi)存節(jié)點best。⑷處的條件表示,當(dāng)內(nèi)存節(jié)點未使用,節(jié)點大小滿足申請的內(nèi)存大小,并且內(nèi)存節(jié)點的大小小于已遍歷的最佳節(jié)點時,把當(dāng)前節(jié)點設(shè)置為最佳的節(jié)點。⑸如果節(jié)點大小剛剛好,跳轉(zhuǎn)到SIZE_MATCH標(biāo)簽。

      ⑹如果最佳節(jié)點為空,表示申請失敗,返回。⑺處處理最佳節(jié)點的大小超出申請的大小的情況,⑻處表示去掉申請的內(nèi)存塊后剩余的內(nèi)存節(jié)點node,然后標(biāo)記該剩余節(jié)點的信息,包含未使用標(biāo)記,大小,上一個節(jié)點等信息。⑼如果最佳內(nèi)存節(jié)點不是尾節(jié)點,執(zhí)行⑽后續(xù)下一個節(jié)點,把下一個內(nèi)存節(jié)點的上一個節(jié)點設(shè)置為剩余內(nèi)存節(jié)點node。⑾表示如果最佳節(jié)點是尾節(jié)點,則把剩余節(jié)點設(shè)置為尾節(jié)點。⑿處的SIZE_MATCH把申請的內(nèi)存節(jié)點設(shè)置為已使用,然后把返回指針設(shè)置為節(jié)點的數(shù)據(jù)區(qū)起始地址best->data。

      VOID *OsHeapAlloc(VOID *pool, UINT32 size) { struct LosHeapNode *node = NULL; struct LosHeapNode *next = NULL; struct LosHeapNode *best = NULL; VOID *ptr = NULL; ⑴ UINT32 alignSize = ALIGNE(size); ⑵ struct LosHeapManager *heapMan = HEAP_CAST(struct LosHeapManager *, pool); if ((heapMan == NULL) || (size > MALLOC_MAXSIZE)) { return NULL; } if (OsHeapIntegrityCheck(heapMan) != LOS_OK) { return NULL; } ⑶ node = heapMan->tail; while (node != NULL) { ⑷ if ((node->used == 0) && (node->size >= alignSize) && ((best == NULL) || (best->size > node->size))) { best = node; ⑸ if (best->size == alignSize) { goto SIZE_MATCH; } } node = node->prev; } ⑹ if (best == NULL) { PRINT_ERR("there's not enough whole to alloc 0x%x Bytes!\n", alignSize); goto OUT; } ⑺ if ((best->size - alignSize) > sizeof(struct LosHeapNode)) { ⑻ node = (struct LosHeapNode*)(UINTPTR)(best->data + alignSize); node->used = 0; node->size = best->size - alignSize - sizeof(struct LosHeapNode); node->prev = best; ⑼ if (best != heapMan->tail) { ⑽ next = OsHeapPrvGetNext(heapMan, node); if (next != NULL) { next->prev = node; } } else { ⑾ heapMan->tail = node; } best->size = alignSize; } SIZE_MATCH: ⑿ best->align = 0; best->used = 1; ptr = best->data; OsHeapStatAddUsed(heapMan, best); OUT: return ptr; }

      2.1.3 按指定字節(jié)內(nèi)存對齊申請內(nèi)存OsHeapAllocAlign

      函數(shù)VOID* OsHeapAllocAlign(VOID *pool, UINT32 size, UINT32 boundary)用于從指定動態(tài)內(nèi)存池中申請長度為size且地址按boundary字節(jié)對齊的內(nèi)存。該函數(shù)需要3個參數(shù),VOID *pool為內(nèi)存池起始地址,UINT32 size為需要申請的內(nèi)存大小,UINT32 boundary內(nèi)存對齊數(shù)值。當(dāng)使用函數(shù)OsHeapAlloc()申請內(nèi)存后得到的內(nèi)存地址VOID *ptr,對齊后的內(nèi)存地址為VOID *alignedPtr,二者的偏移值使用UINT32 gapSize保存。下面分析下源碼。

      ⑴處對參數(shù)進行校驗,內(nèi)存池地址不能為空,申請的內(nèi)存大小不能為0,對齊字節(jié)boundary不能小于4,且按自身進行對齊。⑵處計算對齊后需要申請的內(nèi)存大小,⑶處調(diào)用函數(shù)申請到內(nèi)存VOID *ptr,然后計算出對齊的內(nèi)存地址VOID *alignedPtr,如果二者相等則返回。⑷處計算出對齊內(nèi)存的偏移值,對偏移值設(shè)置對齊標(biāo)記,然后執(zhí)行⑸把偏移值保存在內(nèi)存VOID *alignedPtr的前4個字節(jié)里。重新定向要返回的指針,完成申請對齊的內(nèi)存。

      VOID* OsHeapAllocAlign(VOID *pool, UINT32 size, UINT32 boundary) { UINT32 useSize; UINT32 gapSize; VOID *ptr = NULL; VOID *alignedPtr = NULL; ⑴ if ((pool == NULL) || (size == 0) || (boundary < sizeof(VOID *)) || !IS_ALIGNED(boundary, boundary)) { return NULL; } ⑵ useSize = (size + boundary) - sizeof(VOID*); if (useSize < size) { return NULL; } ⑶ ptr = OsHeapAlloc(pool, useSize); if (ptr != NULL) { alignedPtr = (VOID *)(UINTPTR)OS_MEM_ALIGN(ptr, boundary); if (alignedPtr == ptr) { goto OUT; } ⑷ gapSize = (UINTPTR)alignedPtr - (UINTPTR)ptr; OS_MEM_SET_ALIGN_FLAG(gapSize); ⑸ *((UINT32 *)((UINTPTR)alignedPtr - sizeof(UINTPTR))) = gapSize; ptr = alignedPtr; } OUT: return ptr; }

      2.1.4 釋放內(nèi)存OsHeapFree

      BOOL OsHeapFree(VOID *pool, VOID *ptr)函數(shù)用于從內(nèi)存池釋放內(nèi)存,需要2個參數(shù),VOID *pool是初始化過的靜態(tài)內(nèi)存池地址。VOID *ptr是需要釋放的動態(tài)內(nèi)存塊的數(shù)據(jù)區(qū)的起始地址,注意這個不是內(nèi)存控制節(jié)點的地址。下面分析下源碼。

      ⑴處轉(zhuǎn)換內(nèi)存池起始地址獲取管理節(jié)點,然后對傳入的參數(shù)先進行校驗。⑵處獲取偏移值gapSize。⑶處如果偏移值設(shè)置了對齊標(biāo)記,表示是要釋放使用OsHeapAllocAlign()函數(shù)申請的內(nèi)存。⑷去除對齊標(biāo)記,獲取實際的偏移值,然后計算出內(nèi)存的實際未對齊的指針ptr。如果是使用OsHeapAlloc()申請的內(nèi)存,不存在偏移值,gapSize其實就是內(nèi)存管理節(jié)點結(jié)構(gòu)體的成員變量size的值。

      繼續(xù)執(zhí)行⑸進行校驗,如果指針*ptr比頭節(jié)點還小,或比尾節(jié)點還大,則報錯返回。⑹基于數(shù)據(jù)地址獲取節(jié)點地址,然后下一步對該節(jié)點進行驗證,如果驗證失敗則返回錯誤。調(diào)用⑺處函數(shù)OsHeapDoFree()進行內(nèi)存釋放。

      BOOL OsHeapFree(VOID *pool, VOID *ptr) { struct LosHeapNode *node = NULL; UINT32 gapSize; BOOL ret = TRUE; ⑴ struct LosHeapManager *heapMan = HEAP_CAST(struct LosHeapManager *, pool); if ((heapMan == NULL) || (ptr == NULL)) { return LOS_NOK; } ⑵ gapSize = *((UINT32 *)((UINTPTR)ptr - sizeof(UINTPTR))); ⑶ if (OS_MEM_GET_ALIGN_FLAG(gapSize)) { ⑷ gapSize = OS_MEM_GET_ALIGN_GAPSIZE(gapSize); ptr = (VOID *)((UINTPTR)ptr - gapSize); } ⑸ if (((UINTPTR)ptr < (UINTPTR)heapMan->head) || ((UINTPTR)ptr > ((UINTPTR)heapMan->tail + sizeof(struct LosHeapNode)))) { PRINT_ERR("0x%lx out of range!\n", (UINTPTR)ptr); return FALSE; } ⑹ node = ((struct LosHeapNode *)ptr) - 1; if ((node->used == 0) || (!((UINTPTR)node == (UINTPTR)heapMan->head) && (((UINTPTR)node->prev < (UINTPTR)heapMan->head) || ((UINTPTR)node->prev > ((UINTPTR)heapMan->tail + sizeof(struct LosHeapNode))) || ((UINTPTR)OsHeapPrvGetNext(heapMan, node->prev) != (UINTPTR)node)))) { ret = FALSE; goto OUT; } OsHeapStatDecUsed(heapMan, node); ⑺ OsHeapDoFree(heapMan, node); OUT: return ret; }

      我們看下函數(shù)OsHeapDoFree()。⑴把要釋放的節(jié)點設(shè)置為未使用狀態(tài),⑵處循環(huán)遍歷要釋放的節(jié)點前序節(jié)點,指向第一個未使用的節(jié)點,然后執(zhí)行⑶獲取第一個未使用的節(jié)點的下一個節(jié)點。⑷從第一未使用的節(jié)點循環(huán)遍歷后續(xù)每一個節(jié)點。⑸如果遍歷到使用中的節(jié)點,把使用的節(jié)點的前序節(jié)點設(shè)置為node,然后跳出循環(huán)。⑹如果遍歷的節(jié)點都是未使用的節(jié)點,則把大小合并到第一個節(jié)點。⑺如果遍歷到尾節(jié)點,則更新尾節(jié)點為node。⑻更新循環(huán)條件,持續(xù)獲取下一個節(jié)點。

      STATIC VOID OsHeapDoFree(struct LosHeapManager *heapMan, struct LosHeapNode *curNode) { struct LosHeapNode *node = curNode; struct LosHeapNode *next = NULL; ⑴ node->used = 0; ⑵ while ((node->prev) && (!node->prev->used)) { node = node->prev; } ⑶ next = OsHeapPrvGetNext(heapMan, node); ⑷ while (next != NULL) { ⑸ if (next->used) { next->prev = node; break; } ⑹ node->size += (sizeof(struct LosHeapNode) + next->size); ⑺ if (heapMan->tail == next) { heapMan->tail = node; } ⑻ next = OsHeapPrvGetNext(heapMan, node); } }

      2.2 動態(tài)內(nèi)存對外操作接口

      2.2.1 內(nèi)存初始化LOS_MemInit

      我們分析下初始化動態(tài)內(nèi)存池函數(shù)UINT32 LOS_MemInit(VOID *pool, UINT32 size)的代碼。我們先看看函數(shù)參數(shù),VOID *pool是動態(tài)內(nèi)存池的起始地址,UINT32 size是初始化的動態(tài)內(nèi)存池的總大小,size需要小于等于*pool開始的內(nèi)存區(qū)域的大小,否則會影響后面的內(nèi)存區(qū)域,還需要大于動態(tài)內(nèi)存管理節(jié)點大小sizeof(struct LosHeapManager)。

      我們看下代碼,⑴處對傳入?yún)?shù)進行校驗。⑵處調(diào)用函數(shù)OsHeapInit()進行內(nèi)存池初始化,這是初始化的內(nèi)存的核心函數(shù)。⑶處開啟宏LOSCFG_KERNEL_MEM_SLAB_EXTENTION支持時,才會執(zhí)行。

      LITE_OS_SEC_TEXT_INIT UINT32 LOS_MemInit(VOID *pool, UINT32 size) { UINT32 ret = LOS_NOK; UINT32 intSave; ⑴ if ((pool == NULL) || (size <= sizeof(struct LosHeapManager))) { return ret; } MEM_LOCK(intSave); if (OsMemMulPoolInit(pool, size) != LOS_OK) { goto OUT; } ⑵ if (OsHeapInit(pool, size) == FALSE) { (VOID)OsMemMulPoolDeinit(pool); goto OUT; } ⑶ OsSlabMemInit(pool, size); ret = LOS_OK; OUT: MEM_UNLOCK(intSave); LOS_TRACE(MEM_INFO_REQ, pool); return ret; }

      2.2.2 申請動態(tài)內(nèi)存LOS_MemAlloc

      初始化動態(tài)內(nèi)存池后,我們可以使用函數(shù)VOID *LOS_MemAlloc(VOID *pool, UINT32 size)來申請動態(tài)內(nèi)存,下面分析下源碼。

      ⑴處對參數(shù)進行校驗,內(nèi)存池地址不能為空,申請的內(nèi)存大小不能為0。⑵處如果支持SLAB,則先嘗試從SLAB中獲取內(nèi)存,否則執(zhí)行⑶調(diào)用函數(shù)OsHeapAlloc(pool, size)申請內(nèi)存塊。

      LITE_OS_SEC_TEXT VOID *LOS_MemAlloc(VOID *pool, UINT32 size) { VOID *ptr = NULL; UINT32 intSave; ⑴ if ((pool == NULL) || (size == 0)) { return ptr; } MEM_LOCK(intSave); ⑵ ptr = OsSlabMemAlloc(pool, size); if (ptr == NULL) { ⑶ ptr = OsHeapAlloc(pool, size); } MEM_UNLOCK(intSave); LOS_TRACE(MEM_ALLOC, pool, (UINTPTR)ptr, size); return ptr; }

      2.2.3 按指定字節(jié)對齊申請動態(tài)內(nèi)存LOS_MemAllocAlign

      我們還可以使用函數(shù)VOID *LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary),從指定動態(tài)內(nèi)存池中申請長度為size且地址按boundary字節(jié)對齊的內(nèi)存。該函數(shù)需要3個參數(shù),VOID *pool為內(nèi)存池起始地址,UINT32 size為需要申請的內(nèi)存大小,UINT32 boundary內(nèi)存對齊數(shù)值。當(dāng)申請內(nèi)存后得到的內(nèi)存地址VOID *ptr,對齊后的內(nèi)存地址為VOID *alignedPtr,二者的偏移值使用UINT32 gapSize保存。代碼如下,可以看出實際申請內(nèi)存使用的是函數(shù)OsHeapAllocAlign()。

      LITE_OS_SEC_TEXT VOID *LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary) { VOID *ptr = NULL; UINT32 intSave; MEM_LOCK(intSave); ptr = OsHeapAllocAlign(pool, size, boundary); MEM_UNLOCK(intSave); LOS_TRACE(MEM_ALLOC_ALIGN, pool, (UINTPTR)ptr, size, boundary); return ptr; }

      2.2.4 釋放動態(tài)內(nèi)存

      對申請的內(nèi)存塊使用完畢,我們可以使用函數(shù)UINT32 LOS_MemFree(VOID *pool, VOID *ptr)來釋放靜態(tài)內(nèi)存,需要2個參數(shù),VOID *pool是初始化過的靜態(tài)內(nèi)存池地址。VOID *ptr是需要釋放的動態(tài)內(nèi)存塊的數(shù)據(jù)區(qū)的起始地址,注意這個不是內(nèi)存控制節(jié)點的地址。下面分析下源碼。

      ⑴處對傳入的參數(shù)先進行校驗。⑵如果內(nèi)存是從SLAB中申請的內(nèi)存,需要釋放到SLAB內(nèi)存區(qū)。⑶處調(diào)用函數(shù)OsHeapFree(pool, mem)完成內(nèi)存的釋放。

      LITE_OS_SEC_TEXT UINT32 LOS_MemFree(VOID *pool, VOID *mem) { BOOL ret = FALSE; UINT32 intSave; ⑴ if ((pool == NULL) || (mem == NULL)) { return LOS_NOK; } MEM_LOCK(intSave); ⑵ ret = OsSlabMemFree(pool, mem); if (ret != TRUE) { ⑶ ret = OsHeapFree(pool, mem); } MEM_UNLOCK(intSave); LOS_TRACE(MEM_FREE, pool, (UINTPTR)mem); return (ret == TRUE ? LOS_OK : LOS_NOK); }

      2.2.5 重新申請動態(tài)內(nèi)存

      我們還可以使用函數(shù)VOID *LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size),按size大小重新分配內(nèi)存塊,并將原內(nèi)存塊內(nèi)容拷貝到新內(nèi)存塊。如果新內(nèi)存塊申請成功,則釋放原內(nèi)存塊。該函數(shù)需要3個參數(shù),VOID *pool為內(nèi)存池起始地址,VOID *ptr為之前申請的內(nèi)存地址,UINT32 size為重新申請的內(nèi)存大小。下面分析下源碼。

      ⑴處如果內(nèi)存池地址不為空,入size為0,等價于函數(shù)LOS_MemFree()。⑵處如果傳入的內(nèi)存地址為空,則等價于LOS_MemAlloc()函數(shù)。⑶處獲取內(nèi)存對齊偏移值,并校驗是否包含對齊標(biāo)記,如果內(nèi)存對齊函數(shù)LOS_MemAllocAlign()申請的內(nèi)存,則返回錯誤,無法重現(xiàn)申請內(nèi)存。⑷處獲取內(nèi)存節(jié)點地址,⑸計算出需要復(fù)制的內(nèi)存空間的大小。⑹按指定的大小申請內(nèi)存,然后執(zhí)行⑺復(fù)制內(nèi)存數(shù)據(jù)。⑻處如果復(fù)制數(shù)據(jù)成功則釋放之前的內(nèi)存,如果復(fù)制失敗則釋放重新申請的內(nèi)存。執(zhí)行⑼釋放掉內(nèi)存。

      VOID *LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size) { VOID *retPtr = NULL; VOID *freePtr = NULL; UINT32 intSave; struct LosHeapNode *node = NULL; UINT32 cpySize; UINT32 gapSize; errno_t rc; ⑴ if ((ptr != NULL) && (size == 0)) { if (LOS_MemFree(pool, ptr) != LOS_OK) { PRINT_ERR("LOS_MemFree error, pool[%p], pPtr[%p]\n", pool, ptr); } ⑵ } else if (ptr == NULL) { retPtr = LOS_MemAlloc(pool, size); } else { MEM_LOCK(intSave); UINT32 oldSize = OsSlabMemCheck(pool, ptr); if (oldSize != (UINT32)(-1)) { cpySize = (size > oldSize) ? oldSize : size; } else { ⑶ gapSize = *((UINTPTR *)((UINTPTR)ptr - sizeof(UINTPTR))); if (OS_MEM_GET_ALIGN_FLAG(gapSize)) { MEM_UNLOCK(intSave); return NULL; } ⑷ node = ((struct LosHeapNode *)ptr) - 1; ⑸ cpySize = (size > (node->size)) ? (node->size) : size; } MEM_UNLOCK(intSave); ⑹ retPtr = LOS_MemAlloc(pool, size); if (retPtr != NULL) { ⑺ rc = memcpy_s(retPtr, size, ptr, cpySize); ⑻ if (rc == EOK) { freePtr = ptr; } else { freePtr = retPtr; retPtr = NULL; } ⑼ if (LOS_MemFree(pool, freePtr) != LOS_OK) { PRINT_ERR("LOS_MemFree error, pool[%p], ptr[%p]\n", pool, freePtr); } } } LOS_TRACE(MEM_REALLOC, pool, (UINTPTR)ptr, size); return retPtr; }

      小結(jié)

      本文帶領(lǐng)大家一起剖析了LiteOS動態(tài)內(nèi)存模塊bestfit_little算法的源代碼,包含動態(tài)內(nèi)存的結(jié)構(gòu)體、動態(tài)內(nèi)存池初始化、動態(tài)內(nèi)存申請、釋放等。感謝閱讀,如有任何問題、建議,都可以留言給我們: https://gitee.com/LiteOS/LiteOS/issues 。為了更容易找到LiteOS代碼倉,建議訪問 https://gitee.com/LiteOS/LiteOS ,關(guān)注Watch、Star、并Fork到自己賬戶下,謝謝。

      輕量級操作系統(tǒng) LiteOS

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:Python爬蟲:想聽榜單歌曲?使用BeautifulSoup庫只需要14行代碼即可搞定
      下一篇:安卓端云文檔如何新建文件夾?(云文檔文件夾怎么保存到本地)
      相關(guān)文章
      亚洲jjzzjjzz在线观看| 亚洲第一视频网站| 亚洲国产精品综合久久网各| 久久亚洲国产精品五月天| 亚洲熟妇av一区二区三区| jlzzjlzz亚洲乱熟在线播放| 亚洲精品国自产拍在线观看| 日产国产精品亚洲系列| 国产成人亚洲毛片| 亚洲av无码乱码在线观看野外 | 亚洲视频在线免费| 亚洲日本中文字幕一区二区三区| 亚洲乱亚洲乱少妇无码| 亚洲欧洲日产国码一级毛片| 婷婷综合缴情亚洲狠狠尤物| 亚洲国产天堂久久久久久| 亚洲一区二区三区无码影院| 国产亚洲精品不卡在线| 亚洲日韩精品一区二区三区| 亚洲国产精品特色大片观看完整版| 亚洲va久久久噜噜噜久久| 亚洲精品线在线观看| 亚洲精品免费在线| 亚洲伊人久久大香线蕉| 亚洲一区精彩视频| 亚洲AV无码成人精品区狼人影院| 在线观看亚洲视频| 精品国产亚洲男女在线线电影 | 亚洲天堂男人影院| 亚洲AV无码一区二区三区性色| 色综合久久精品亚洲国产| 亚洲国产精品第一区二区三区| 中文字幕在线亚洲精品| 亚洲AV无码一区二区三区DV | 亚洲高清视频在线播放| 久久精品亚洲AV久久久无码| 亚洲变态另类一区二区三区| 亚洲精品高清在线| 亚洲乱码一区二区三区在线观看| 亚洲嫩草影院久久精品| 亚洲va成无码人在线观看|