鸿蒙轻内核M核源码分析系列九 动态内存Dynamic Memory 第三部分 非连续性内存

网友投稿 747 2022-05-30

鸿蒙轻内核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:

支持多段非连续内存,相关代码使能。用户配置多段内存区域,调用接口

鸿蒙轻内核M核源码分析系列九 动态内存Dynamic Memory 第三部分 非连续性内存

LOS_MemRegionsAdd(VOID *pool, const LosMemRegion * const multipleMemRegions)进行内存池合一:

如果pool为空,则合并到主内存堆m_aucSysMem0。

如果不为空,则初始化一个新的内存池,合并多内存区域为一个从堆。

/** * @ingroup los_memory * @brief Initialize multiple non-continuous memory regions. * * @par Description: *

    *
  • This API is used to initialize multiple non-continuous memory regions. If the starting address of a pool is specified, * the memory regions will be linked to the pool as free nodes. Otherwise, the first memory region will be initialized as a * new pool, and the rest regions will be linked as free nodes to the new pool.
  • *
* * @attention *
    *
  • If the starting address of a memory pool is specified, the start address of the non-continuous memory regions should be * greater than the end address of the memory pool.
  • *
  • The multiple non-continuous memory regions shouldn't conflict with each other.
  • *
* * @param pool [IN] The memory pool address. If NULL is specified, the start address of first memory region will be * initialized as the memory pool address. If not NULL, it should be a valid address of a memory pool. * @param memRegions [IN] The LosMemRegion array that contains multiple non-continuous memory regions. The start address * of the memory regions are placed in ascending order. * @param memRegionCount [IN] The count of non-continuous memory regions, and it should be the length of the LosMemRegion array. * * @retval #LOS_NOK The multiple non-continuous memory regions fails to be initialized. * @retval #LOS_OK The multiple non-continuous memory regions is initialized successfully. * @par Dependency: *
    *
  • los_memory.h: the header file that contains the API declaration.
  • *
* @see None. */ extern UINT32 LOS_MemRegionsAdd(VOID *pool, const LosMemRegion * const memRegions, UINT32 memRegionCount);

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小时内删除侵权内容。

上一篇:对.clang-format的部分注释
下一篇:编译器与解释器的区别和工作原理
相关文章