In-depth analysis of how the dynamic memory of the Hongmeng light core M core supports multiple non-contiguous memory

华为云开发者社区
中文
Abstract: light core M core newly supports multiple discontinuous memory areas, which logically unifies multiple discontinuous memories, and users do not perceive different memory blocks at the bottom.

This article is shared from the Huawei Cloud Community " Light Kernel M Core Source Code Analysis Series Nine Dynamic Memory Dynamic Memory Supplement ", author: zhushy.

Some chips cannot meet the requirements of the on-chip RAM size and need to use off-chip physical memory for expansion. For multiple segments of non-contiguous memory, the memory management module needs to be managed uniformly. When the application uses the memory interface, it does not need to pay attention to which piece of physical memory the memory is allocated, and does not perceive multiple pieces of memory.

Multi-segment non-contiguous memory is shown in the figure below:
image.png

Hongmeng light core M core newly supports multiple discontinuous memory areas, which logically unifies multiple discontinuous memories, and users do not perceive different memory blocks at the bottom. This article analyzes the source code of the dynamic memory module that supports multiple non-contiguous memory to help readers master its use. The source code involved in this article, taking the OpenHarmony LiteOS-M kernel as an example, can be obtained at the open source site https://gitee.com/openharmony/kernel_liteos_m . Next, let's look at the source code of the newly added structure, macro, and external interface.

1. Structure definition and common macro definition

A new structure LosMemRegion is added to the file kernel/include/los_memory.h to maintain multiple non-contiguous memory regions, including the start address and size of each memory region. as follows:

typedef struct {
    VOID *startAddress; /* 内存区域的开始地址 */
    UINT32 length;      /* 内存区域的长度 */
} LosMemRegion;

It should be noted that the definition of this structure takes effect only when the macro LOSCFG_MEM_MUL_REGIONS is turned on. This macro is also a configuration macro that supports non-contiguous memory areas and is defined in the file kernel/include/los_config.h.

Let's continue to look at several new macro functions, defined in the file kernel/src/mm/los_memory.c, the code is as follows:

The comments are more clear. When LOSCFG_MEM_MUL_REGIONS is enabled to support the discontinuous memory feature, the gap gap between the two discontinuous memory regions will be marked as a virtual used memory node. Of course, this node cannot be released, nor can it be counted in the memory commissioning feature. Because we just treat it as a used memory node, but it is not. In the dynamic memory algorithm, each memory node maintains a pointer to the previous node. For the virtual used node, we set the pointer as a magic word to mark it as an interval part of the memory area.

(1) A magic word OS_MEM_GAP_NODE_MAGIC is defined at ⑴, which is used to represent the gap Gap area before two discontinuous memory areas. ⑵ and ⑶ define two macros, which are respectively used to set magic words and verify magic words.

#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. Common operations of dynamic memory

In this section, we analyze the implementation algorithm of discontinuous memory and the interface implementation code together. First understand the algorithm through the schematic diagram:
image.png

Set diagram, we understand the steps of combining discontinuous memory into a memory pool:

1. Call LOS_MemInit to initialize the first memory area of the multi-segment memory area
2. Get the start address and length of the next memory area, and calculate the gapSize between this memory area and the previous memory area.
3. Regard the interval part of the memory block as a virtual used node, use the tail node of the previous memory block and set its size to gapSize + OS_MEM_NODE_HEAD_SIZE.
4. Divide the current memory area into a free memory block and a tail node, and insert the free memory block into the free linked list. And set the front and back link relationship of each node.
5. If there are more non-contiguous memory blocks, repeat steps 2-4 above.

2.1 New interface LOS_MemRegionsAdd

The interface description document of the newly added interface is shown below, the comments are more detailed, and the summary is as follows:

  • LOSCFG_MEM_MUL_REGIONS=0:

Multi-segment non-contiguous memory is not supported, and related codes are not enabled.

  • LOSCFG_MEM_MUL_REGIONS=1:

Supports multiple segments of non-contiguous memory, and related codes are enabled. The user configures multiple memory areas and calls the interface
LOS_MemRegionsAdd(VOID pool, const LosMemRegion const multipleMemRegions) to unify the memory pool:

  • If the pool is empty, it is merged into the main memory heap m_aucSysMem0.
  • If it is not empty, initialize a new memory pool and merge multiple memory regions into one slave heap.
/**
 * @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 Implementation of the new interface LOS_MemRegionsAdd

Combining the above schematic diagram and adding notes, the realization is clearer, just read the code directly.

#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

summary

This article leads everyone to analyze how the dynamic memory of the Hongmeng light core M core supports multiple non-contiguous memory, including structure, operation diagram, new interface and so on. Thanks for reading. If you have any questions or suggestions, you can leave a comment, thank you.

more learning content, please click to follow the IoT community , add Huawei Cloud IoT assistant WeChat account (hwc-iot) for more information

Click to follow, and learn about the fresh technology of Huawei Cloud for the first time~

阅读 486

开发者之家
华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

1.2k 声望
1.7k 粉丝
0 条评论

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

1.2k 声望
1.7k 粉丝
文章目录
宣传栏