Abstract: static memory is essentially a static array. The block size in the static memory pool is set during initialization, and the block size cannot be changed after initialization. The static memory pool consists of a control block and several memory blocks of the same size. The control block is located at the head of the memory pool and is used for memory block management. The application and release of memory blocks are based on the block size.

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

The memory management module manages the memory resources of the system. It is one of the core modules of the operating system and mainly includes the initialization, allocation and release of the memory.

During the operation of the system, the memory management module manages the use of memory by users and OS through the application/release of the memory, so as to optimize the utilization and efficiency of the memory, and at the same time solve the problem of memory fragmentation in the system to the greatest extent.

The memory management of Hongmeng Light Kernel is divided into static memory management and dynamic memory management, providing functions such as memory initialization, allocation, and release.

  • dynamic memory: allocates a user-specified memory block in the dynamic memory pool.
    Advantages: Allocate on demand.
    Disadvantages: Fragmentation may occur in the memory pool.
  • static memory: allocates a memory block of a preset (fixed) size during user initialization in the static memory pool.
    Advantages: high allocation and release efficiency, no fragmentation in the static memory pool.
    Disadvantages: You can only apply for the memory block of the initialized preset size, and cannot apply on demand.

This article mainly analyzes the Hongmeng light kernel static memory (Memory Box), and subsequent series will continue to analyze dynamic memory. Static memory is essentially a static array. The block size in the static memory pool is set during initialization, and the block size cannot be changed after initialization. The static memory pool consists of a control block and several memory blocks of the same size. The control block is located at the head of the memory pool and is used for memory block management. The application and release of memory blocks are based on the block size.

This article helps readers master the use of static memory by analyzing the source code of the static memory module. The source code involved in this article, taking the OpenHarmony LiteOS-M kernel as an example, can be obtained from the open source site https://gitee.com/openharmony/kernel_liteos_m .

Next, let's look at the structure of static memory, static memory initialization, and source code for common operations of static memory.

1. Static memory structure definition and common macro definition

1.1 Static memory structure definition

The static memory structure is defined in the file kernel\include\los_membox.h. The source code is as follows, (1) the static memory node LOS_MEMBOX_NODE structure is defined, the static memory structure pool information structure defined in (2) is LOS_MEMBOX_INFO, and the structure members are explained in the comment section.

⑴  typedef struct tagMEMBOX_NODE {
        struct tagMEMBOX_NODE *pstNext; /**< 静态内存池中空闲节点指针,指向下一个空闲节点 */
    } LOS_MEMBOX_NODE;

⑵  typedef struct LOS_MEMBOX_INFO {
        UINT32 uwBlkSize;               /**< 静态内存池中空闲节点指针,指向下一个空闲节点 */
        UINT32 uwBlkNum;                /**< 静态内存池的内存块总数量 */
        UINT32 uwBlkCnt;                /**< 静态内存池的已分配的内存块总数量 */
    #if (LOSCFG_PLATFORM_EXC == 1)
        struct LOS_MEMBOX_INFO *nextMemBox; /**< 指向下一个静态内存池 */
    #endif
        LOS_MEMBOX_NODE stFreeList;     /**< 静态内存池的空闲内存块单向链表 */
    } LOS_MEMBOX_INFO;

The static memory usage is described in the following schematic diagram. For a static memory area, the header is LOS_MEMBOX_INFO information, followed by each memory block. The size of each memory block is uwBlkSize, including the memory block node LOS_MEMBOX_NODE and the memory block data area. The free memory block node points to the next free memory block node.

1.2 Definitions of commonly used macros for static memory

Some important macro definitions are also provided in the static memory header file. (1) LOS_MEMBOX_ALIGNED(memAddr) is used to align the memory address, (2) OS_MEMBOX_NEXT(addr, blkSize) obtains the memory address of the next memory block according to the current node memory address addr and the memory block size blkSize. (3) OS_MEMBOX_NODE_HEAD_SIZE indicates the size of the node header in the memory block. Each memory block contains the memory node LOS_MEMBOX_NODE and the data area for storing services. ⑷ indicates the total size of static memory, including the size occupied by the memory pool information structure and the size occupied by each memory block.

⑴  #define LOS_MEMBOX_ALIGNED(memAddr) (((UINTPTR)(memAddr) + sizeof(UINTPTR) - 1) & (~(sizeof(UINTPTR) - 1)))

⑵  #define OS_MEMBOX_NEXT(addr, blkSize) (LOS_MEMBOX_NODE *)(VOID *)((UINT8 *)(addr) + (blkSize))

⑶  #define OS_MEMBOX_NODE_HEAD_SIZE sizeof(LOS_MEMBOX_NODE)

⑷  #define LOS_MEMBOX_SIZE(blkSize, blkNum) \
    (sizeof(LOS_MEMBOX_INFO) + (LOS_MEMBOX_ALIGNED((blkSize) + OS_MEMBOX_NODE_HEAD_SIZE) * (blkNum)))

Some macros and inline functions are also defined in the file kernel\src\mm\los_membox.c. ⑴ Define OS_MEMBOX_MAGIC magic word, the last 8 bits of this 32-bit magic word maintain task number information, and the task number bit is defined by the macro at ⑵. The macro at ⑶ defines the maximum value of the task number, and the macro at ⑷ extracts the task number information from the magic word.

(5) The magic word is set in the inline function. After the memory block node is allocated from the static memory pool, the node pointer .pstNext no longer points to the next free memory block node, but is set to the magic word. The inline function at ⑹ is used to verify the magic word. The macro at ⑺ obtains the data area address of the memory block according to the node address of the memory block, and the macro at ⑻ obtains the node address of the memory block according to the data area address of the memory block.

⑴  #define OS_MEMBOX_MAGIC         0xa55a5a00

⑵  #define OS_MEMBOX_TASKID_BITS   8

⑶  #define OS_MEMBOX_MAX_TASKID    ((1 << OS_MEMBOX_TASKID_BITS) - 1)

⑷  #define OS_MEMBOX_TASKID_GET(addr) (((UINTPTR)(addr)) & OS_MEMBOX_MAX_TASKID)

⑸  STATIC INLINE VOID OsMemBoxSetMagic(LOS_MEMBOX_NODE *node)
    {
        UINT8 taskID = (UINT8)LOS_CurTaskIDGet();
        node->pstNext = (LOS_MEMBOX_NODE *)(OS_MEMBOX_MAGIC | taskID);
    }

⑹  STATIC INLINE UINT32 OsMemBoxCheckMagic(LOS_MEMBOX_NODE *node)
    {
        UINT32 taskID = OS_MEMBOX_TASKID_GET(node->pstNext);
        if (taskID > (LOSCFG_BASE_CORE_TSK_LIMIT + 1)) {
            return LOS_NOK;
        } else {
            return (node->pstNext == (LOS_MEMBOX_NODE *)(OS_MEMBOX_MAGIC | taskID)) ? LOS_OK : LOS_NOK;
        }
    }

⑺  #define OS_MEMBOX_USER_ADDR(addr) \
        ((VOID *)((UINT8 *)(addr) + OS_MEMBOX_NODE_HEAD_SIZE))

⑻  #define OS_MEMBOX_NODE_ADDR(addr) \
        ((LOS_MEMBOX_NODE *)(VOID *)((UINT8 *)(addr) - OS_MEMBOX_NODE_HEAD_SIZE))

2. Common operations of static memory

When users need to use fixed-length memory, they can obtain the memory through static memory allocation. Once used, the occupied memory is returned through the static memory release function so that it can be reused.

2.1 Initialize the static memory pool

We analyze the code to initialize the static memory pool function UINT32 LOS_MemboxInit (VOID pool, UINT32 poolSize, UINT32 blkSize). Let's take a look at the function parameters first. VOID pool is the starting address of the static memory pool, UINT32 poolSize is the total size of the initialized static memory pool, poolSize needs to be less than or equal to the size of the memory area starting with *pool, otherwise it will affect the subsequent memory area. It also needs to be larger than the static memory header size sizeof(LOS_MEMBOX_INFO). The length UINT32 blkSize is the block size of each memory block in the static memory pool.

Let's look at the code, check the incoming parameters at (1). (2) Set the actual size of each memory block in the static memory pool, which has been memory aligned, and also counts the node information in the memory block. (3) Calculate the total number of memory blocks in the memory pool, and then set the number of used memory blocks. uwBlkCnt to 0.
(4) If the available memory block is 0, return initialization failure. (5) Get the first free memory block node in the memory pool. ⑹ Mount the free memory block on the static memory pool information structure free memory block linked list stFreeList.pstNext, and then execute ⑺ Each free memory block points to the next free memory block in turn, and is linked.

UINT32 LOS_MemboxInit(VOID *pool, UINT32 poolSize, UINT32 blkSize)
{
    LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;
    LOS_MEMBOX_NODE *node = NULL;
    UINT32 index;
    UINT32 intSave;

⑴  if (pool == NULL) {
        return LOS_NOK;
    }

    if (blkSize == 0) {
        return LOS_NOK;
    }

    if (poolSize < sizeof(LOS_MEMBOX_INFO)) {
        return LOS_NOK;
    }

    MEMBOX_LOCK(intSave);
⑵  boxInfo->uwBlkSize = LOS_MEMBOX_ALIGNED(blkSize + OS_MEMBOX_NODE_HEAD_SIZE);
    if (boxInfo->uwBlkSize == 0) {
        MEMBOX_UNLOCK(intSave);
        return LOS_NOK;
    }
⑶  boxInfo->uwBlkNum = (poolSize - sizeof(LOS_MEMBOX_INFO)) / boxInfo->uwBlkSize;
    boxInfo->uwBlkCnt = 0;
⑷  if (boxInfo->uwBlkNum == 0) {
        MEMBOX_UNLOCK(intSave);
        return LOS_NOK;
    }

⑸  node = (LOS_MEMBOX_NODE *)(boxInfo + 1);

⑹  boxInfo->stFreeList.pstNext = node;

⑺  for (index = 0; index < boxInfo->uwBlkNum - 1; ++index) {
        node->pstNext = OS_MEMBOX_NEXT(node, boxInfo->uwBlkSize);
        node = node->pstNext;
    }

    node->pstNext = NULL;

#if (LOSCFG_PLATFORM_EXC == 1)
    OsMemBoxAdd(pool);
#endif

    MEMBOX_UNLOCK(intSave);

    return LOS_OK;
}

2.2 Clear the contents of the static memory block

We can use the function VOID LOS_MemboxClr (VOID pool, VOID box) to clear the contents of the data area in the static memory block. Two parameters are required. VOID pool is the initialized static memory pool address. VOID box is the starting address of the data area of the static memory block whose content needs to be cleared. Note that this is not the node address of the memory block, and the node area of each memory block cannot be cleared. The source code is analyzed below.

⑴ Check the parameters, ⑵ call the memset_s() function to write 0 in the data area of the memory block. The starting address of writing is the starting address VOID *box of the data area of the memory block, and the writing length is the length of the data area boxInfo->uwBlkSize-OS_MEMBOX_NODE_HEAD_SIZE.

VOID LOS_MemboxClr(VOID *pool, VOID *box)
{
    LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;

⑴  if ((pool == NULL) || (box == NULL)) {
        return;
    }

⑵  (VOID)memset_s(box, (boxInfo->uwBlkSize - OS_MEMBOX_NODE_HEAD_SIZE), 0,
                   (boxInfo->uwBlkSize - OS_MEMBOX_NODE_HEAD_SIZE));
}

2.3 Apply and release static memory

After initializing the static memory pool, we can use the function VOID LOS_MemboxAlloc (VOID pool) to apply for static memory. The source code is analyzed below.

(1) Get the head node of the linked list of free memory blocks in the static memory pool. If the linked list is not empty, execute (2) to assign the next available node to nodeTmp. (3) The head node of the linked list executes the next node of the next linked list, and then executes (4) to set the magic word for the allocated memory block, and then adds 1 to the number of used memory blocks in the memory pool. ⑸ When returning, call the macro OS_MEMBOX_USER_ADDR() to calculate the data area geology of the memory block.

VOID *LOS_MemboxAlloc(VOID *pool)
{
    LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;
    LOS_MEMBOX_NODE *node = NULL;
    LOS_MEMBOX_NODE *nodeTmp = NULL;
    UINT32 intSave;

    if (pool == NULL) {
        return NULL;
    }

    MEMBOX_LOCK(intSave);
⑴  node = &(boxInfo->stFreeList);
    if (node->pstNext != NULL) {
⑵      nodeTmp = node->pstNext;
⑶      node->pstNext = nodeTmp->pstNext;
⑷      OsMemBoxSetMagic(nodeTmp);
        boxInfo->uwBlkCnt++;
    }
    MEMBOX_UNLOCK(intSave);

⑸  return (nodeTmp == NULL) ? NULL : OS_MEMBOX_USER_ADDR(nodeTmp);
}

After using the requested memory block, we can use the function UINT32 LOS_MemboxFree (VOID pool, VOID box) to release the static memory. Two parameters are required. VOID pool is the initialized static memory pool address. VOID box is the starting address of the data area of the static memory block that needs to be released. Note that this is not the node address of the memory block. The source code is analyzed below.

(1) Obtain the node address node according to the data area address of the memory block to be released. (2) Check the memory block to be released first. (3) Hang the memory block to be released on the linked list of free memory blocks in the memory pool, and then execute ⑷ to reduce the used amount by 1.

LITE_OS_SEC_TEXT UINT32 LOS_MemboxFree(VOID *pool, VOID *box)
{
    LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;
    UINT32 ret = LOS_NOK;
    UINT32 intSave;

    if ((pool == NULL) || (box == NULL)) {
        return LOS_NOK;
    }

    MEMBOX_LOCK(intSave);
    do {
⑴      LOS_MEMBOX_NODE *node = OS_MEMBOX_NODE_ADDR(box);
⑵      if (OsCheckBoxMem(boxInfo, node) != LOS_OK) {
            break;
        }

⑶      node->pstNext = boxInfo->stFreeList.pstNext;
        boxInfo->stFreeList.pstNext = node;
⑷      boxInfo->uwBlkCnt--;
        ret = LOS_OK;
    } while (0);
    MEMBOX_UNLOCK(intSave);

    return ret;
}

Next, let's take a look at the check function OsCheckBoxMem(). ⑴ If the block size of the memory pool is 0, return the verification failure. (2) Calculate the offset of the memory fast node to be released relative to the first memory block node. (3) If the remainder of the offset divided by the number of memory blocks is not 0, the verification fails. ⑷ If the quotient of the offset divided by the number of memory blocks is greater than or equal to the number of memory blocks, return the verification failure. ⑸ Call the macro OsMemBoxCheckMagic to check the magic word.

STATIC INLINE UINT32 OsCheckBoxMem(const LOS_MEMBOX_INFO *boxInfo, const VOID *node)
{
    UINT32 offset;

⑴  if (boxInfo->uwBlkSize == 0) {
        return LOS_NOK;
    }

⑵  offset = (UINT32)((UINTPTR)node - (UINTPTR)(boxInfo + 1));
⑶  if ((offset % boxInfo->uwBlkSize) != 0) {
        return LOS_NOK;
    }

⑷  if ((offset / boxInfo->uwBlkSize) >= boxInfo->uwBlkNum) {
        return LOS_NOK;
    }

⑸   return OsMemBoxCheckMagic((LOS_MEMBOX_NODE *)node);
}

summary

This article led everyone to analyze the source code of the static memory module of the Hongmeng Light Kernel, including the structure of static memory, static memory pool initialization, static memory application, release, and clear content. There will be more sharing articles in the follow-up, so stay tuned. You are also welcome to share your experience of learning and using the light kernel. If you have any questions or suggestions, you can leave a message to us: 160d96c71705f4 https://gitee.com/openharmony /kernel_liteos_m/issues . light kernel code warehouse easier, it is recommended to visit 160d96c71705f8 https://gitee.com/openharmony/kernel_liteos_m , follow Watch, like Star, and Fork to your account, thank you.

Click to follow, and get to know the fresh technology of


华为云开发者联盟
1.4k 声望1.8k 粉丝

生于云,长于云,让开发者成为决定性力量