微吼云上線多路互動直播服務 加速多場景互動直播落地
912
2025-03-31
鴻蒙輕內核M核源碼分析系列其他文章:
鴻蒙輕內核源碼分析系列一 前言
鴻蒙輕內核M核源碼分析系列二 數據結構-雙向循環鏈表
鴻蒙輕內核M核源碼分析系列三 數據結構-任務就緒隊列
鴻蒙輕內核M核源碼分析系列四 數據結構-任務排序鏈表
鴻蒙輕內核M核源碼分析系列五 中斷Hwi
鴻蒙輕內核M核源碼分析系列六 時間管理
鴻蒙輕內核M核源碼分析系列七 任務及任務調度(1)任務棧
鴻蒙輕內核M核源碼分析系列七 任務及任務調度(2)任務模塊
鴻蒙輕內核M核源碼分析系列七 任務及任務調度 (3)任務調度模塊
鴻蒙輕內核M核源碼分析系列八 靜態內存Static Memory
鴻蒙輕內核M核源碼分析系列九 動態內存Dynamic Memory(1)
鴻蒙輕內核M核源碼分析系列九 動態內存Dynamic Memory(2)
鴻蒙輕內核M核源碼分析系列九 動態內存Dynamic Memory(3)
鴻蒙輕內核M核源碼分析系列十 互斥鎖Mutex
鴻蒙輕內核M核源碼分析系列十一 信號量Semaphore
鴻蒙輕內核M核源碼分析系列十二 事件Event
鴻蒙輕內核M核源碼分析系列十三 消息隊列Queue
鴻蒙輕內核M核源碼分析系列十三(續) 消息隊列QueueMail接口
鴻蒙輕內核M核源碼分析系列十四 軟件定時器Swtmr
鴻蒙輕內核M核源碼分析系列十五 CPU使用率CPUP (1)
鴻蒙輕內核M核源碼分析系列十五 CPU使用率CPUP (2)
鴻蒙輕內核M核源碼分析系列九 動態內存Dynamic Memory 補充
一些芯片片內RAM大小無法滿足要求,需要使用片外物理內存進行擴充。對于多段非連續性內存,需要內存管理模塊統一管理,應用使用內存接口時不需要關注內存分配屬于哪塊物理內存,不感知多塊內存。
多段非連續性內存如下圖所示:
鴻蒙輕內核M核新增支持了多段非連續性內存區域,把多個非連續性內存邏輯上合一,用戶不感知底層的不同內存塊。本文來分析下動態內存模塊的支持多段非連續內存的源碼,幫助讀者掌握其使用。本文中所涉及的源碼,以OpenHarmony LiteOS-M內核為例,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 獲取。接下來,我們看下新增的結構體、宏和對外接口的源代碼。
1、結構體定義和常用宏定義
在文件kernel/include/los_memory.h中新增了結構體LosMemRegion用于維護多個非連續的內存區域,包含各個內存區域的開始地址和大小。如下:
typedef struct { VOID *startAddress; /* 內存區域的開始地址 */ UINT32 length; /* 內存區域的長度 */ } LosMemRegion;
需要注意這個結構體的定義需要開啟宏LOSCFG_MEM_MUL_REGIONS的情況下才生效,這個宏也是支持非連續內存區域的配置宏,定義在文件kernel/include/los_config.h中。
我們繼續看下新增的幾個宏函數,定義在文件kernel/src/mm/los_memory.c,代碼下下文:
注釋講的比較明白,當開啟LOSCFG_MEM_MUL_REGIONS支持非連續內存特性時,會把兩個不連續內存區域之間的間隔Gap區域標記為虛擬的已使用內存節點。這個節點當然不能被釋放,在內存調測特性中也不能被統計。因為我們只是把它視為已使用內存節點,但其實不是。在動態內存算法中每個內存節點都維護一個指向前序節點的指針,對于虛擬已使用節點,我們把該指針設置為魔術字,來標記它是個內存區域的間隔部分。
⑴處定義了一個魔術字OS_MEM_GAP_NODE_MAGIC,用于表示兩個不連續內存區域之前的間隔Gap區域。⑵和⑶處定義2個宏,分別用于設置魔術字,驗證魔術字。
#if (LOSCFG_MEM_MUL_REGIONS == 1) /** * When LOSCFG_MEM_MUL_REGIONS is enabled to support multiple non-continuous memory regions, the gap between two memory regions * is marked as a used OsMemNodeHead node. The gap node could not be freed, and would also be skipped in some DFX functions. The * 'ptr.prev' pointer of this node is set to OS_MEM_GAP_NODE_MAGIC to identify that this is a gap node. */ ⑴ #define OS_MEM_GAP_NODE_MAGIC 0xDCBAABCD ⑵ #define OS_MEM_MARK_GAP_NODE(node) (((struct OsMemNodeHead *)(node))->ptr.prev = (struct OsMemNodeHead *)OS_MEM_GAP_NODE_MAGIC) ⑶ #define OS_MEM_IS_GAP_NODE(node) (((struct OsMemNodeHead *)(node))->ptr.prev == (struct OsMemNodeHead *)OS_MEM_GAP_NODE_MAGIC) #else ⑵ #define OS_MEM_MARK_GAP_NODE(node) ⑶ #define OS_MEM_IS_GAP_NODE(node) FALSE #endif
2、動態內存常用操作
本節我們一起分析下非連續性內存的實現算法,及接口實現代碼。首先通過示意圖了解下算法:
集合示意圖,我們了解下非連續性內存合并為一個內存池的步驟:
1、把多段內存區域的第一塊內存區域調用LOS_MemInit進行初始化
2、獲取下一個內存區域的開始地址和長度,計算該內存區域和上一塊內存區域的間隔大小gapSize。
3、把內存塊間隔部分視為虛擬的已使用節點,使用上一內存塊的尾節點,設置其大小為gapSize+ OS_MEM_NODE_HEAD_SIZE。
4、把當前內存區域劃分為一個空閑內存塊和一個尾節點,把空閑內存塊插入到空閑鏈表。并設置各個節點的前后鏈接關系。
5、有更多的非連續內存塊,重復上述步驟2-4。
2.1 新增接口LOS_MemRegionsAdd
新增的接口的接口說明文檔見下文,注釋比較詳細,總結如下:
LOSCFG_MEM_MUL_REGIONS=0:
不支持多段非連續內存,相關代碼不使能。
LOSCFG_MEM_MUL_REGIONS=1:
支持多段非連續內存,相關代碼使能。用戶配置多段內存區域,調用接口
LOS_MemRegionsAdd(VOID *pool, const LosMemRegion * const multipleMemRegions)進行內存池合一:
如果pool為空,則合并到主內存堆m_aucSysMem0。
如果不為空,則初始化一個新的內存池,合并多內存區域為一個從堆。
/** * @ingroup los_memory * @brief Initialize multiple non-continuous memory regions. * * @par Description: *
2.1 新增接口LOS_MemRegionsAdd實現
結合上文示意圖,加上注釋,實現比較清晰,直接閱讀下代碼即可。
#if (LOSCFG_MEM_MUL_REGIONS == 1) STATIC INLINE UINT32 OsMemMulRegionsParamCheck(VOID *pool, const LosMemRegion * const memRegions, UINT32 memRegionCount) { const LosMemRegion *memRegion = NULL; VOID *lastStartAddress = NULL; VOID *curStartAddress = NULL; UINT32 lastLength; UINT32 curLength; UINT32 regionCount; if ((pool != NULL) && (((struct OsMemPoolHead *)pool)->info.pool != pool)) { PRINT_ERR("wrong mem pool addr: %p, func: %s, line: %d\n", pool, __FUNCTION__, __LINE__); return LOS_NOK; } if (pool != NULL) { lastStartAddress = pool; lastLength = ((struct OsMemPoolHead *)pool)->info.totalSize; } memRegion = memRegions; regionCount = 0; while (regionCount < memRegionCount) { curStartAddress = memRegion->startAddress; curLength = memRegion->length; if ((curStartAddress == NULL) || (curLength == 0)) { PRINT_ERR("Memory address or length configured wrongly:address:0x%x, the length:0x%x\n", (UINTPTR)curStartAddress, curLength); return LOS_NOK; } if (((UINTPTR)curStartAddress & (OS_MEM_ALIGN_SIZE - 1)) || (curLength & (OS_MEM_ALIGN_SIZE - 1))) { PRINT_ERR("Memory address or length configured not aligned:address:0x%x, the length:0x%x, alignsize:%d\n", \ (UINTPTR)curStartAddress, curLength, OS_MEM_ALIGN_SIZE); return LOS_NOK; } if ((lastStartAddress != NULL) && (((UINT8 *)lastStartAddress + lastLength) >= (UINT8 *)curStartAddress)) { PRINT_ERR("Memory regions overlapped, the last start address:0x%x, the length:0x%x, the current start address:0x%x\n", \ (UINTPTR)lastStartAddress, lastLength, (UINTPTR)curStartAddress); return LOS_NOK; } memRegion++; regionCount++; lastStartAddress = curStartAddress; lastLength = curLength; } return LOS_OK; } STATIC INLINE VOID OsMemMulRegionsLink(struct OsMemPoolHead *poolHead, VOID *lastStartAddress, UINT32 lastLength, struct OsMemNodeHead *lastEndNode, const LosMemRegion *memRegion) { UINT32 curLength; UINT32 gapSize; struct OsMemNodeHead *curEndNode = NULL; struct OsMemNodeHead *curFreeNode = NULL; VOID *curStartAddress = NULL; curStartAddress = memRegion->startAddress; curLength = memRegion->length; // mark the gap between two regions as one used node gapSize = (UINT8 *)(curStartAddress) - ((UINT8 *)(lastStartAddress) + lastLength); lastEndNode->sizeAndFlag = gapSize + OS_MEM_NODE_HEAD_SIZE; OS_MEM_SET_MAGIC(lastEndNode); OS_MEM_NODE_SET_USED_FLAG(lastEndNode->sizeAndFlag); // mark the gap node with magic number OS_MEM_MARK_GAP_NODE(lastEndNode); poolHead->info.totalSize += (curLength + gapSize); poolHead->info.totalGapSize += gapSize; curFreeNode = (struct OsMemNodeHead *)curStartAddress; curFreeNode->sizeAndFlag = curLength - OS_MEM_NODE_HEAD_SIZE; curFreeNode->ptr.prev = lastEndNode; OS_MEM_SET_MAGIC(curFreeNode); OsMemFreeNodeAdd(poolHead, (struct OsMemFreeNodeHead *)curFreeNode); curEndNode = OS_MEM_END_NODE(curStartAddress, curLength); curEndNode->sizeAndFlag = 0; curEndNode->ptr.prev = curFreeNode; OS_MEM_SET_MAGIC(curEndNode); OS_MEM_NODE_SET_USED_FLAG(curEndNode->sizeAndFlag); #if (LOSCFG_MEM_WATERLINE == 1) poolHead->info.curUsedSize += OS_MEM_NODE_HEAD_SIZE; poolHead->info.waterLine = poolHead->info.curUsedSize; #endif } UINT32 LOS_MemRegionsAdd(VOID *pool, const LosMemRegion *const memRegions, UINT32 memRegionCount) { UINT32 ret; UINT32 lastLength; UINT32 curLength; UINT32 regionCount; struct OsMemPoolHead *poolHead = NULL; struct OsMemNodeHead *lastEndNode = NULL; struct OsMemNodeHead *firstFreeNode = NULL; const LosMemRegion *memRegion = NULL; VOID *lastStartAddress = NULL; VOID *curStartAddress = NULL; ret = OsMemMulRegionsParamCheck(pool, memRegions, memRegionCount); if (ret != LOS_OK) { return ret; } memRegion = memRegions; regionCount = 0; if (pool != NULL) { // add the memory regions to the specified memory pool poolHead = (struct OsMemPoolHead *)pool; lastStartAddress = pool; lastLength = poolHead->info.totalSize; } else { // initialize the memory pool with the first memory region lastStartAddress = memRegion->startAddress; lastLength = memRegion->length; poolHead = (struct OsMemPoolHead *)lastStartAddress; ret = LOS_MemInit(lastStartAddress, lastLength); if (ret != LOS_OK) { return ret; } memRegion++; regionCount++; } firstFreeNode = OS_MEM_FIRST_NODE(lastStartAddress); lastEndNode = OS_MEM_END_NODE(lastStartAddress, lastLength); while (regionCount < memRegionCount) { // traverse the rest memory regions, and initialize them as free nodes and link together curStartAddress = memRegion->startAddress; curLength = memRegion->length; OsMemMulRegionsLink(poolHead, lastStartAddress, lastLength, lastEndNode, memRegion); lastStartAddress = curStartAddress; lastLength = curLength; lastEndNode = OS_MEM_END_NODE(curStartAddress, curLength); memRegion++; regionCount++; } firstFreeNode->ptr.prev = lastEndNode; return ret; } #endif
小結
本文帶領大家一起剖析了鴻蒙輕內核M核的動態內存如何支持多段非連續性內存,包含結構體、運作示意圖、新增接口等等。感謝閱讀,如有任何問題、建議,都可以留言評論,謝謝。
IoT 數據結構 虛擬化 輕量級操作系統 LiteOS
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。