关于harmonyos:深层剖析鸿蒙轻内核M核的动态内存如何支持多段非连续性内存

4次阅读

共计 8196 个字符,预计需要花费 21 分钟才能阅读完成。

摘要:鸿蒙轻内核 M 核新增反对了多段非连续性内存区域,把多个非连续性内存逻辑上合一,用户不感知底层的不同内存块。

本文分享自华为云社区《鸿蒙轻内核 M 核源码剖析系列九 动态内存 Dynamic Memory 补充》,作者:zhushy。

一些芯片片内 RAM 大小无奈满足要求,须要应用片外物理内存进行裁减。对于多段非连续性内存,须要内存治理模块对立治理,利用应用内存接口时不须要关注内存调配属于哪块物理内存,不感知多块内存。

多段非连续性内存如下图所示:

鸿蒙轻内核 M 核新增反对了多段非连续性内存区域,把多个非连续性内存逻辑上合一,用户不感知底层的不同内存块。本文来剖析下动静内存模块的反对多段非间断内存的源码,帮忙读者把握其应用。本文中所波及的源码,以 OpenHarmony LiteOS- M 内核为例,均能够在开源站点 https://gitee.com/openharmony… 获取。接下来,咱们看下新增的构造体、宏和对外接口的源代码。

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:
 * <ul>
 * <li>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.</li>
 * </ul>
 * 
 * @attention
 * <ul>
 * <li>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.</li>
 * <li>The multiple non-continuous memory regions shouldn't conflict with each other.</li>
 * </ul>
 *
 * @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:
 * <ul>
 * <li>los_memory.h: the header file that contains the API declaration.</li>
 * </ul>
 * @see None.
 */
extern UINT32 LOS_MemRegionsAdd(VOID *pool, const LosMemRegion * const memRegions, UINT32 memRegionCount);

2.2 新增接口 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 物联网社区,增加华为云 IoT 小助手微信号(hwc-iot),获取更多资讯

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0