1
Abstract: This article led everyone to analyze the source code of the dynamic memory module of the Hongmeng Light Kernel, including the structure of dynamic memory, dynamic memory pool initialization, dynamic memory application, release, etc.

This article is shared from Huawei Cloud Community " Light Kernel M Core Source Code Analysis Series Nine Dynamic Memory Dynamic 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 the OS through the application/release of 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 memory block of a user-specified size 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.

The previous series analyzed static memory, and we began to analyze dynamic memory. Dynamic memory management is mainly used in scenarios where users need to use memory blocks of varying sizes. When users need to use memory, they can request a memory block of a specified size through the dynamic memory application function of the operating system. Once used, the occupied memory is returned through the dynamic memory release function so that it can be reused.

OpenHarmony LiteOS-M's dynamic memory optimizes the division of intervals based on the TLSF algorithm to obtain better performance and reduce the fragmentation rate. The block diagram of the dynamic memory core algorithm is as follows:
image.png

According to the size of the free memory block, multiple free linked lists are used for management. According to the size of the free memory block, it is divided into two parts: [4, 127] and [27, 231], as shown in the size class above:

  • The memory in the interval [4,127] is equally divided, as shown in the green part of the figure above, divided into 31 cells, and the corresponding memory block size between each cell is a multiple of 4 bytes. Each cell corresponds to a free memory linked list and a bit used to mark whether the corresponding free memory linked list is empty. When the value is 1, the free linked list is not empty. [4,127] The memory of the interval is marked with a 32-bit unsigned integer bitmap.
  • For free memory blocks larger than 127 bytes, the free linked list is managed according to the size of the power-of-two interval. It is divided into 24 cells in total, and each cell is equally divided into 8 secondary cells, as shown in the blue part of Size Class and Size SubClass in the above figure. Each secondary cell corresponds to a free linked list and a bit used to mark whether the corresponding free memory linked list is empty. A total of 24*8=192 secondary cells, corresponding to 192 free linked lists and 192/32=6 32-bit unsigned integer bitmap tags.

For example, when there is 40 bytes of free memory that needs to be inserted into the free linked list, corresponding to the inter-cell [40,43], the 10th free linked list, the 10th bit of the bitmap mark. Mount the 40-byte free memory on the 10th free linked list, and determine whether the bitmap flag needs to be updated. When 40 bytes of memory need to be applied for, a free linked list with a memory block meeting the application size is obtained according to the bitmap mark, and free memory nodes are obtained from the free linked list. If the allocated node is larger than the memory size that needs to be applied for, the split node operation is performed, and the remaining nodes are remounted to the corresponding free linked list. When there is 580 bytes of free memory to be inserted into the free linked list, corresponding to the secondary cell [2^9,2^9+2^6], the 31st+2*8=47 free linked list, the second bitmap The 17th bit of the mark. Mount the 580 bytes of free memory on the 47th free linked list, and determine whether the bitmap flag needs to be updated. When it is necessary to apply for 580 bytes of memory, a free linked list with a memory block meeting the application size is obtained according to the bitmap mark, and free memory nodes are obtained from the free linked list. If the allocated node is larger than the memory size that needs to be applied for, the split node operation is performed, and the remaining nodes are remounted to the corresponding free linked list. If the corresponding free linked list is empty, the larger memory range is used to query whether there is a free linked list that satisfies the conditions. In actual calculation, the free linked list that meets the application size will be found at one time.

The dynamic memory management structure is shown in the following figure:
image.png

  • The head of the memory pool

The pool header part of the memory pool contains memory pool information, an array of bitmap tags, and an array of free linked lists. The memory pool information includes the starting address of the memory pool, the total size of the heap area, and the attributes of the memory pool. The bitmap tag array is composed of 7 32-bit unsigned integers, and each bit indicates whether the corresponding free linked list is mounted with a free memory block node. The free memory linked list contains 223 free memory head node information, and each free memory head node information maintains the memory node head and the predecessor and successor free memory nodes in the free linked list.

  • Memory pool node part

Contains 3 types of nodes, unused free memory nodes, used memory nodes, and tail nodes. Each memory node maintains a preorder pointer that points to the previous memory node in the memory pool, maintains the size and usage mark, and marks the size and use of the memory node. The data field behind the free memory node and the used memory node, the tail node has no data field.

This article helps readers master the use of dynamic memory by analyzing the source code of the dynamic 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 dynamic memory, dynamic memory initialization, and the source code for common operations of dynamic memory.

1. Dynamic memory structure definition and common macro definition

1.1 Dynamic memory structure definition

Dynamic memory structures include dynamic memory pool information structure OsMemPoolInfo, dynamic memory pool header structure OsMemPoolHead, dynamic memory node header structure OsMemNodeHead, used memory node structure OsMemUsedNodeHead, and free memory node structure OsMemFreeNodeHead. These structures are defined in the file kernel\src\mm\los_memory.c. The member variables of each structure will be explained below in conjunction with the above dynamic memory management structure diagram.

1.1.1 Dynamic memory pool header related structure

The dynamic memory pool information structure OsMemPoolInfo maintains the starting address and size information of the memory pool. The three main members are the start address of the memory pool.pool, the size of the memory pool.poolSize and the memory value attribute.attr. If the macro LOSCFG_MEM_WATERLINE is turned on, the waterline value of the memory pool will also be maintained.

struct OsMemPoolInfo {
    VOID *pool;               /* 内存池的内存开始地址 */
    UINT32 totalSize;         /* 内存池总大小 */
    UINT32 attr;              /* 内存池属性 */
#if (LOSCFG_MEM_WATERLINE == 1)
    UINT32 waterLine;         /* 内存池中内存最大使用值 */
    UINT32 curUsedSize;       /* 内存池中当前已使用的大小 */
#endif
};

The source code of the dynamic memory pool header structure OsMemPoolHead is as follows. In addition to the dynamic memory pool information structure struct OsMemPoolInfo info, it also maintains two arrays, one is the free memory linked list bitmap array freeListBitmap[], and the other is the free memory linked list array freeList[]. The macro definitions OS_MEM_BITMAP_WORDS and OS_MEM_FREE_LIST_COUNT will be introduced later.

struct OsMemPoolHead {
    struct OsMemPoolInfo info;
    UINT32 freeListBitmap[OS_MEM_BITMAP_WORDS];
    struct OsMemFreeNodeHead *freeList[OS_MEM_FREE_LIST_COUNT];
#if (LOSCFG_MEM_MUL_POOL == 1)
    VOID *nextPool;
#endif
};

1.1.2 Dynamic memory pool memory node related structure

First look at the definition of the dynamic memory node header structure OsMemNodeHead. (1) If the macro LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK for memory node integrity check is turned on, the magic word .magic will be maintained for verification. (2) If the memory leak check macro is turned on, the link register array linkReg[] will be maintained. The member variable at ⑶ is a pointer combination, and each memory node head in the memory pool maintains a pointer to execute the previous memory node. (4) Maintain the size and tag information of the memory node.

struct OsMemNodeHead {
  #if (LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK == 1)
⑴    UINT32 magic;
  #endif
  #if (LOSCFG_MEM_LEAKCHECK == 1)
⑵    UINTPTR linkReg[LOSCFG_MEM_RECORD_LR_CNT];
  #endif
      union {
          struct OsMemNodeHead *prev; /* The prev is used for current node points to the previous node */
          struct OsMemNodeHead *next; /* The next is used for sentinel node points to the expand node */
⑶     } ptr;
 #if (LOSCFG_MEM_FREE_BY_TASKID == 1)
⑷    UINT32 taskID : 6;
      UINT32 sizeAndFlag : 26;
  #else
      UINT32 sizeAndFlag;
  #endif
  };

Next, look at the used memory node structure OsMemUsedNodeHead. The structure is relatively simple, and the dynamic memory node head structure OsMemNodeHead is directly used as the only member.

struct OsMemUsedNodeHead {
    struct OsMemNodeHead header;
};

Let's look at the free memory node structure OsMemFreeNodeHead. In addition to the OsMemNodeHead member of the dynamic memory node header structure, it also contains two pointers to the previous and next free memory nodes.

struct OsMemFreeNodeHead {
    struct OsMemNodeHead header;
    struct OsMemFreeNodeHead *prev;
    struct OsMemFreeNodeHead *next;
};

1.2 Macros and functions related to the dynamic memory core algorithm

The dynamic memory also provides some macro definitions and inline functions related to the TLSF algorithm. These macros are very important. You need to be familiar with the definitions of these macros before analyzing the source code. It can be combined with the above dynamic memory core algorithm block diagram for learning. The macro pair at ⑴ is in [2^n,2^(n+1)], where the large memory block in the interval (n=7,8,...30) is divided into 2^3=8 equal parts. The macro at ⑵ defines that the small memory block in the interval [4,127] is divided into 31, namely 4, 8, 12, ..., 124. (3) The upper bound of small memory is defined. Considering memory alignment and granularity, the maximum value can only be 124.

The macro at ⑷ indicates that it is in [2^n,2^(n+1)], where the large memory in the interval (n=7,8,...30) is divided into 24 cells, where n=7 is defined at ⑺ The macro OS_MEM_LARGE_START_BUCKET. ⑻ corresponds to the length of the free memory linked list. ⑼ is the length of the free linked list bitmap array, 31 small memory uses 1 bitmap word, so it needs to be increased by 1. ⑽ Define the bitmap mask, and each bitmap word is a 32-bit unsigned integer.

Continue to look at inline functions. ⑾The function finds the first bit of 1 in the bitmap word. The function of this implementation is similar to the built-in function __builtin_ctz. This function is used to get the bitmap word corresponding to the free memory linked list, the first free memory linked list with the free memory block mounted. ⑿ Get the last 1 bit in the bitmap word, (from the 32-bit binary value from left to right, the 0th, 1, ..., 31st). ⒀Log in the function name is the abbreviation of logarithm in English. The function is used to calculate the integer part of the logarithm with the base 2. ⒁ Get the size level number of the memory interval. For those smaller than 128 bytes, there are 31 levels, and the pair is in [2^n,2^(n+1)], where (n=7,8,...30) interval There are 24 levels of memory. ⒂According to the memory size, the first-level number of the memory interval obtains the number of the second-level cell, and the pair is in [2^n,2^(n+1)], where the memory in the interval (n=7,8,...30) , There are 8 secondary districts.

    /* The following is the macro definition and interface implementation related to the TLSF. */

    /* Supposing a Second Level Index: SLI = 3. */
⑴  #define OS_MEM_SLI                      3
    /* Giving 1 free list for each small bucket: 4, 8, 12, up to 124. */
⑵  #define OS_MEM_SMALL_BUCKET_COUNT       31
⑶  #define OS_MEM_SMALL_BUCKET_MAX_SIZE    128
    /* Giving 2^OS_MEM_SLI free lists for each large bucket. */
⑷  #define OS_MEM_LARGE_BUCKET_COUNT       24
    /* OS_MEM_SMALL_BUCKET_MAX_SIZE to the power of 2 is 7. */
⑺  #define OS_MEM_LARGE_START_BUCKET       7

    /* The count of free list. */
⑻  #define OS_MEM_FREE_LIST_COUNT  (OS_MEM_SMALL_BUCKET_COUNT + (OS_MEM_LARGE_BUCKET_COUNT << OS_MEM_SLI))
    /* The bitmap is used to indicate whether the free list is empty, 1: not empty, 0: empty. */
⑼  #define OS_MEM_BITMAP_WORDS     ((OS_MEM_FREE_LIST_COUNT >> 5) + 1)

⑽  #define OS_MEM_BITMAP_MASK 0x1FU

    /* Used to find the first bit of 1 in bitmap. */
⑾  STATIC INLINE UINT16 OsMemFFS(UINT32 bitmap)
    {
        bitmap &= ~bitmap + 1;
        return (OS_MEM_BITMAP_MASK - CLZ(bitmap));
    }

    /* Used to find the last bit of 1 in bitmap. */
⑿  STATIC INLINE UINT16 OsMemFLS(UINT32 bitmap)
    {
        return (OS_MEM_BITMAP_MASK - CLZ(bitmap));
    }

⒀  STATIC INLINE UINT32 OsMemLog2(UINT32 size)
    {
        return (size > 0) ? OsMemFLS(size) : 0;
    }

    /* Get the first level: f = log2(size). */
⒁  STATIC INLINE UINT32 OsMemFlGet(UINT32 size)
    {
        if (size < OS_MEM_SMALL_BUCKET_MAX_SIZE) {
            return ((size >> 2) - 1); /* 2: The small bucket setup is 4. */
        }
        return (OsMemLog2(size) - OS_MEM_LARGE_START_BUCKET + OS_MEM_SMALL_BUCKET_COUNT);
    }

    /* Get the second level: s = (size - 2^f) * 2^SLI / 2^f. */
⒂  STATIC INLINE UINT32 OsMemSlGet(UINT32 size, UINT32 fl)
    {
        if ((fl < OS_MEM_SMALL_BUCKET_COUNT) || (size < OS_MEM_SMALL_BUCKET_MAX_SIZE)) {
            PRINT_ERR("fl or size is too small, fl = %u, size = %u\n", fl, size);
            return 0;
        }

        UINT32 sl = (size << OS_MEM_SLI) >> (fl - OS_MEM_SMALL_BUCKET_COUNT + OS_MEM_LARGE_START_BUCKET);
        return (sl - (1 << OS_MEM_SLI));
    }

2. Common operations of dynamic memory

The dynamic memory management module provides users with operations such as initializing and deleting the memory pool, applying for, and releasing dynamic memory. Let's analyze the source code of the interface. Before analyzing the memory operation interface, let's take a look at the commonly used internal interfaces.

2.1 Dynamic memory internal interface

2.1.1 Set and clear the free memory linked list flag bit

(1) The function OsMemSetFreeListBit requires two parameters, one is the head of the memory pool, and the other is the index of the free memory linked list. When a free memory block is mounted on the free memory linked list, the corresponding bit of the bitmap word needs to be set to 1. (1) The function OsMemClearFreeListBit does the opposite operation. When the free memory block is no longer mounted on the free memory linked list, the corresponding bit needs to be cleared.

 STATIC INLINE VOID OsMemSetFreeListBit(struct OsMemPoolHead *head, UINT32 index)
  {
⑴    head->freeListBitmap[index >> 5] |= 1U << (index & 0x1f);
  }

  STATIC INLINE VOID OsMemClearFreeListBit(struct OsMemPoolHead *head, UINT32 index)
  {
⑵    head->freeListBitmap[index >> 5] &= ~(1U << (index & 0x1f));
  }

2.1.2 Merging memory nodes

The function VOID OsMemMergeNode (struct OsMemNodeHead node) is used to merge a given node struct OsMemNodeHead node with its previous free node. (1) Add the size of the previous node to the size of the node to be merged. (2) Get the next node of the given node, and then execute ⑶ to point its previous node to the previous node of the given node to complete the merging of the nodes. The macro OS_MEM_NODE_GET_LAST_FLAG is used to determine whether it is the last node, and the default is 0. You can check the definition of this macro by yourself.

STATIC INLINE VOID OsMemMergeNode(struct OsMemNodeHead *node)
{
    struct OsMemNodeHead *nextNode = NULL;

⑴  node->ptr.prev->sizeAndFlag += node->sizeAndFlag;
⑵  nextNode = (struct OsMemNodeHead *)((UINTPTR)node + node->sizeAndFlag);
    if (!OS_MEM_NODE_GET_LAST_FLAG(nextNode->sizeAndFlag)) {
⑶      nextNode->ptr.prev = node->ptr.prev;
    }
}

2.1.3 Split Memory Node

The function VOID OsMemSplitNode (VOID pool, struct OsMemNodeHead allocNode, UINT32 allocSize) is used to split the memory node and requires three parameters. VOID pool is the starting address of the memory pool, struct OsMemNodeHead allocNode indicates that the required memory is allocated from the memory node, and UINT32 allocSize is the size of the memory that needs to be allocated. The remaining part after the split, if the next node is an idle node, it is merged together. The remaining nodes of the split will be mounted on the free memory linked list.

⑴ means that newFreeNode is the remaining free memory node after allocation, set its previous node as the allocated node, and set the remaining memory size. (2) Adjust the size of the allocated memory, (3) get the next node, and then execute (4) The previous node of the next node is set as the new free node newFreeNode. (5) It is judged whether the next node is used. If it is not used, the next node is deleted from the linked list and then merged with the free node newFreeNode. ⑹ Split the remaining free memory nodes and mount them on the linked list.

STATIC INLINE VOID OsMemSplitNode(VOID *pool, struct OsMemNodeHead *allocNode, UINT32 allocSize)
{
    struct OsMemFreeNodeHead *newFreeNode = NULL;
    struct OsMemNodeHead *nextNode = NULL;

⑴  newFreeNode = (struct OsMemFreeNodeHead *)(VOID *)((UINT8 *)allocNode + allocSize);
    newFreeNode->header.ptr.prev = allocNode;
    newFreeNode->header.sizeAndFlag = allocNode->sizeAndFlag - allocSize;
⑵  allocNode->sizeAndFlag = allocSize;
⑶  nextNode = OS_MEM_NEXT_NODE(&newFreeNode->header);
    if (!OS_MEM_NODE_GET_LAST_FLAG(nextNode->sizeAndFlag)) {
⑷      nextNode->ptr.prev = &newFreeNode->header;
        if (!OS_MEM_NODE_GET_USED_FLAG(nextNode->sizeAndFlag)) {
⑸          OsMemFreeNodeDelete(pool, (struct OsMemFreeNodeHead *)nextNode);
            OsMemMergeNode(nextNode);
        }
    }

⑹  OsMemFreeNodeAdd(pool, newFreeNode);
}

2.1.4 Re-apply for memory

The OsMemReAllocSmaller() function is used to re-apply a smaller memory from a large memory block. The 4 parameters he needs are: VOID pool is the starting address of the memory pool, UINT32 allocSize is the size of the re-applied memory, struct OsMemNodeHead node is the memory node that currently needs to reallocate memory, and UINT32 nodeSize is the size of the current node. ⑴Set the memory node selfNode.sizeAndFlag to the actual size after removing the mark, ⑵Split the node as needed, ⑶Set the used flag after the split node, and complete the application for memory.

STATIC INLINE VOID OsMemReAllocSmaller(VOID *pool, UINT32 allocSize, struct OsMemNodeHead *node, UINT32 nodeSize)
{
#if (LOSCFG_MEM_WATERLINE == 1)
    struct OsMemPoolHead *poolInfo = (struct OsMemPoolHead *)pool;
#endif
⑴  node->sizeAndFlag = nodeSize;
    if ((allocSize + OS_MEM_MIN_LEFT_SIZE) <= nodeSize) {
⑵       OsMemSplitNode(pool, node, allocSize);
#if (LOSCFG_MEM_WATERLINE == 1)
        poolInfo->info.curUsedSize -= nodeSize - allocSize;
#endif
    }
⑶  OS_MEM_NODE_SET_USED_FLAG(node->sizeAndFlag);
#if (LOSCFG_MEM_LEAKCHECK == 1)
    OsMemLinkRegisterRecord(node);
#endif
}

2.1.5 Re-apply for memory by merging node

Finally, let's look at the function OsMemMergeNodeForReAllocBigger(), which is used to merge memory nodes and reallocate larger memory space. It requires 5 parameters, VOID pool is the starting address of the memory pool, UINT32 allocSize is the size of the re-applied memory, struct OsMemNodeHead node is the memory node that needs to reallocate memory currently, UINT32 nodeSize is the size of the current node, struct OsMemNodeHead *nextNode is the next memory node. (1) Set the size of the memory node to the actual size after removing the mark. (2) Delete the next node from the linked list, and then merge the nodes. (3) If the size of the merged node exceeds the size that needs to be re-allocated, the node is split. ⑷ Mark the requested memory node as used, and complete the memory application

STATIC INLINE VOID OsMemMergeNodeForReAllocBigger(VOID *pool, UINT32 allocSize, struct OsMemNodeHead *node,
                                                  UINT32 nodeSize, struct OsMemNodeHead *nextNode)
{
⑴  node->sizeAndFlag = nodeSize;
⑵  OsMemFreeNodeDelete(pool, (struct OsMemFreeNodeHead *)nextNode);
    OsMemMergeNode(nextNode);
    if ((allocSize + OS_MEM_MIN_LEFT_SIZE) <= node->sizeAndFlag) {
⑶       OsMemSplitNode(pool, node, allocSize);
    }
⑷  OS_MEM_NODE_SET_USED_FLAG(node->sizeAndFlag);
    OsMemWaterUsedRecord((struct OsMemPoolHead *)pool, node->sizeAndFlag - nodeSize);
#if (LOSCFG_MEM_LEAKCHECK == 1)
    OsMemLinkRegisterRecord(node);
#endif
}

2.1.6 Related Operations on Free Memory Linked List

Dynamic memory provides several operations for the free memory linked list. Let's analyze the code of these operations in turn. First look at the function OsMemFreeListIndexGet to get the index of the free memory linked list according to the size of the memory node. (1) First obtain the primary index, (2) obtain the secondary index, and then calculate the index of the free linked list and return.

STATIC INLINE UINT32 OsMemFreeListIndexGet(UINT32 size)
{
⑴  UINT32 fl = OsMemFlGet(size);
    if (fl < OS_MEM_SMALL_BUCKET_COUNT) {
        return fl;
    }

⑵  UINT32 sl = OsMemSlGet(size, fl);
    return (OS_MEM_SMALL_BUCKET_COUNT + ((fl - OS_MEM_SMALL_BUCKET_COUNT) << OS_MEM_SLI) + sl);
}

Then look at the function OsMemListAdd, how to insert the free memory node into the free memory linked list. (1) Get the first node of the free linked list. If the node is not empty, set the predecessor node of this node as the node to be inserted. (2) Set the predecessor and successor nodes of the node to be inserted, and then assign the node to the free list pool->freeList[listIndex]. Finally, execute the code at ⑶, set the free linked list bitmap word, and set the magic word.

STATIC INLINE VOID OsMemListAdd(struct OsMemPoolHead *pool, UINT32 listIndex, struct OsMemFreeNodeHead *node)
{
⑴  struct OsMemFreeNodeHead *firstNode = pool->freeList[listIndex];
    if (firstNode != NULL) {
        firstNode->prev = node;
    }
⑵  node->prev = NULL;
    node->next = firstNode;
    pool->freeList[listIndex] = node;
⑶  OsMemSetFreeListBit(pool, listIndex);
    OS_MEM_SET_MAGIC(&node->header);
}

Finally, analyze how the function OsMemListDelete deletes the specified free memory node from the free memory linked list. (1) If the deleted node is the first node of the free memory linked list, the free linked list needs to be executed as the next node of the node to be deleted. If the next node is empty, you need to execute (2) to clear the bitmap word of the free linked list. Otherwise, execute (3) to set the predecessor node of the next node to empty. If the node to be deleted is not the first node in the free list, execute (4) to set the subsequent node of the predecessor node of the node to be deleted as the successor node of the node to be deleted. If the node to be deleted is not the last node, you need to execute (5) to set the predecessor node of the successor node of the node to be deleted as the predecessor node of the node to be deleted. Finally, you need to set the magic word.

STATIC INLINE VOID OsMemListDelete(struct OsMemPoolHead *pool, UINT32 listIndex, struct OsMemFreeNodeHead *node)
{
⑴  if (node == pool->freeList[listIndex]) {
        pool->freeList[listIndex] = node->next;
        if (node->next == NULL) {
⑵          OsMemClearFreeListBit(pool, listIndex);
        } else {
⑶          node->next->prev = NULL;
        }
    } else {
⑷      node->prev->next = node->next;
        if (node->next != NULL) {
⑸          node->next->prev = node->prev;
        }
    }
    OS_MEM_SET_MAGIC(&node->header);
}

2.1.7 Related Operations on Free Memory Nodes

Dynamic memory provides several operations for free memory, such as OsMemFreeNodeAdd, OsMemFreeNodeDelete, OsMemFreeNodeGet.

The function OsMemFreeNodeAdd is used to add a free memory node to the corresponding free memory linked list. (1) Call the function to get the index of the free memory linked list, and then execute (2) to add the free memory node to the free linked list.

STATIC INLINE VOID OsMemFreeNodeAdd(VOID *pool, struct OsMemFreeNodeHead *node)
{
⑴  UINT32 index = OsMemFreeListIndexGet(node->header.sizeAndFlag);
    if (index >= OS_MEM_FREE_LIST_COUNT) {
        LOS_Panic("The index of free lists is error, index = %u\n", index);
    }
⑵  OsMemListAdd(pool, index, node);
}

The function OsMemFreeNodeDelete is used to delete a free memory node from the corresponding free memory linked list. The code is relatively simple, get the index of the free memory linked list, and then call the function OsMemListDelete to delete.

STATIC INLINE VOID OsMemFreeNodeDelete(VOID *pool, struct OsMemFreeNodeHead *node)
{
    UINT32 index = OsMemFreeListIndexGet(node->header.sizeAndFlag);
    OsMemListDelete(pool, index, node);
}

The function OsMemFreeNodeGet obtains the free memory block that meets the size condition according to the memory pool address and the required memory size. (1) Call the function to obtain the memory block that meets the size condition, and then execute (2) to delete the obtained memory block from the free memory linked list, and return the memory node address.

STATIC INLINE struct OsMemNodeHead *OsMemFreeNodeGet(VOID *pool, UINT32 size)
{
    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;
    UINT32 index;
⑴  struct OsMemFreeNodeHead *firstNode = OsMemFindNextSuitableBlock(pool, size, &index);
    if (firstNode == NULL) {
        return NULL;
    }

⑵  OsMemListDelete(poolHead, index, firstNode);

    return &firstNode->header;
}

Finally, analyze the function OsMemFindNextSuitableBlock. (1) Obtain the first-level interval number according to the required memory block size. If the requested memory is in the [4,127] interval, execute (2) to record the index of the free memory linked list. If you need to apply for large memory, execute the code at ⑶. First obtain the secondary interval index, and then calculate the index value index of the free memory linked list. The free memory block may not be mounted in the free memory linked list calculated in this way. Call the function OsMemNotEmptyIndexGet at ⑷ to obtain the free memory linked list index value of the mounted free memory block. If the free memory block that meets the size is successfully obtained, the index value of the free linked list is returned, otherwise the subsequent code continues to be executed. ⑹ Traverse the bitmap words of the free linked list, and the auto-increment variable index in the loop corresponds to the first-level interval number. If the bitmap word is not empty, execute ⑺ to obtain the index of the largest free memory linked list corresponding to this bitmap word.

If the execution reaches point ⑻, it means that no suitable memory block is matched, and a null pointer is returned. ⑼ indicates that there is a free memory linked list that satisfies the size, call the function OsMemFindCurSuitableBlock to obtain a suitable memory block and return. ⑽ The label indicates that the appropriate free memory linked list index is obtained, and the free memory linked list is returned.

STATIC INLINE struct OsMemFreeNodeHead *OsMemFindNextSuitableBlock(VOID *pool, UINT32 size, UINT32 *outIndex)
{
    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;
⑴  UINT32 fl = OsMemFlGet(size);
    UINT32 sl;
    UINT32 index, tmp;
    UINT32 curIndex = OS_MEM_FREE_LIST_COUNT;
    UINT32 mask;

    do {
        if (fl < OS_MEM_SMALL_BUCKET_COUNT) {
⑵          index = fl;
        } else {
⑶          sl = OsMemSlGet(size, fl);
            curIndex = ((fl - OS_MEM_SMALL_BUCKET_COUNT) << OS_MEM_SLI) + sl + OS_MEM_SMALL_BUCKET_COUNT;
            index = curIndex + 1;
        }

⑷      tmp = OsMemNotEmptyIndexGet(poolHead, index);
        if (tmp != OS_MEM_FREE_LIST_COUNT) {
⑸          index = tmp;
            goto DONE;
        }

⑹      for (index = LOS_Align(index + 1, 32); index < OS_MEM_FREE_LIST_COUNT; index += 32) {
            mask = poolHead->freeListBitmap[index >> 5]; /* 5: Divide by 32 to calculate the index of the bitmap array. */
            if (mask != 0) {
⑺              index = OsMemFFS(mask) + index;
                goto DONE;
            }
        }
    } while (0);

⑻  if (curIndex == OS_MEM_FREE_LIST_COUNT) {
        return NULL;
    }

⑼  *outIndex = curIndex;
    return OsMemFindCurSuitableBlock(poolHead, curIndex, size);
DONE:
    *outIndex = index;
⑽  return poolHead->freeList[index];
}

Let's analyze the source code of the function OsMemNotEmptyIndexGet in detail. (1) Obtain the bitmap word according to the free memory linked list index, and (2) Determine whether there is a free memory block that meets the conditions in the secondary small memory interval corresponding to the first level memory interval corresponding to the free memory linked list index. Where index & OS_MEM_BITMAP_MASK only takes the lower 5 bits of the index, you can associate the index value with the bits in the bitmap word. For example, when the index is 39, index & OS_MEM_BITMAP_MASK is equal to 7, which corresponds to the 7th bit of the bitmap word. The expression ~((1 << (index & OS_MEM_BITMAP_MASK))-1) is used to represent the bitmap word corresponding to the index value greater than the free memory linked list index index. (2) After the statement at ⑵ is executed, the mask indicates that the free list index is greater than the value of the bitmap word corresponding to the index of the index. When the mask is not 0, it means that there is a free memory block that meets the memory size, and the code at ⑶ is executed, where OsMemFFS (mask) gets the first bit of 1 in the bitmap word, which corresponds to the mount A linked list of free memory blocks. (index & ~OS_MEM_BITMAP_MASK) Corresponding to the high bit of the linked list index, plus the number of bitmap words to calculate the index value of the free memory linked list that meets the application conditions.

STATIC INLINE UINT32 OsMemNotEmptyIndexGet(struct OsMemPoolHead *poolHead, UINT32 index)
{
⑴  UINT32 mask = poolHead->freeListBitmap[index >> 5]; /* 5: Divide by 32 to calculate the index of the bitmap array. */
⑵  mask &= ~((1 << (index & OS_MEM_BITMAP_MASK)) - 1);
    if (mask != 0) {
⑶      index = OsMemFFS(mask) + (index & ~OS_MEM_BITMAP_MASK);
        return index;
    }

    return OS_MEM_FREE_LIST_COUNT;
}

Finally, look at the function OsMemFindCurSuitableBlock. (1) Loop through the memory blocks mounted on the free memory linked list. If the size of the traversed memory block is larger than the required size, execute (2) to return to the free memory block. Otherwise, a null pointer is returned.

STATIC INLINE struct OsMemFreeNodeHead *OsMemFindCurSuitableBlock(struct OsMemPoolHead *poolHead,
                                        UINT32 index, UINT32 size)
{
    struct OsMemFreeNodeHead *node = NULL;

⑴  for (node = poolHead->freeList[index]; node != NULL; node = node->next) {
        if (node->header.sizeAndFlag >= size) {
⑵           return node;
        }
    }

    return NULL;
}

2.2 Initialize the dynamic memory pool

We analyze the code to initialize the dynamic memory pool function UINT32 LOS_MemInit (VOID pool, UINT32 size). Let’s take a look at the function parameters first. VOID pool is the starting address of the dynamic memory pool, and UINT32 size is the total size of the initialized dynamic memory pool. The size needs to be less than or equal to the size of the memory area starting with *pool, otherwise it will affect the subsequent memory The area also needs to be larger than the minimum value of the dynamic memory pool OS_MEM_MIN_POOL_SIZE. [pool, pool + size] cannot conflict with other memory pools.

Let's look at the code, (1) the incoming parameters are checked, and (2) the incoming parameters are checked for memory alignment. If there is no memory alignment, an error code will be returned. (3) Call the function OsMemPoolInit() to initialize the memory pool, which is the core function of initializing the memory. ⑷ It will only be executed when the macro LOSCFG_MEM_MUL_POOL multi-memory pool support is enabled.

UINT32 LOS_MemInit(VOID *pool, UINT32 size)
{
⑴  if ((pool == NULL) || (size <= OS_MEM_MIN_POOL_SIZE)) {
        return OS_ERROR;
    }

⑵  if (((UINTPTR)pool & (OS_MEM_ALIGN_SIZE - 1)) || \
        (size & (OS_MEM_ALIGN_SIZE - 1))) {
        PRINT_ERR("LiteOS heap memory address or size configured not aligned:address:0x%x,size:0x%x, alignsize:%d\n", \
                  (UINTPTR)pool, size, OS_MEM_ALIGN_SIZE);
        return OS_ERROR;
    }

⑶  if (OsMemPoolInit(pool, size)) {
        return OS_ERROR;
    }

#if (LOSCFG_MEM_MUL_POOL == 1)
⑷  if (OsMemPoolAdd(pool, size)) {
        (VOID)OsMemPoolDeinit(pool);
        return OS_ERROR;
    }
#endif

#if OS_MEM_TRACE
    LOS_TraceReg(LOS_TRACE_MEM_TIME, OsMemTimeTrace, LOS_TRACE_MEM_TIME_NAME, LOS_TRACE_ENABLE);
    LOS_TraceReg(LOS_TRACE_MEM_INFO, OsMemInfoTrace, LOS_TRACE_MEM_INFO_NAME, LOS_TRACE_ENABLE);
#endif

    OsHookCall(LOS_HOOK_TYPE_MEM_INIT, pool, size);

    return LOS_OK;
}

We continue to look at the function OsMemPoolInit(). ⑴ Set the starting address and size of the dynamic memory pool information structure struct OsMemPoolHead *poolHead, ⑵ set the memory pool attribute to be locked and not expandable. (3) Obtain the first memory control node of the memory pool, and then set its size. The size of the node is equal to the total size of the memory pool minus the size of the memory pool pool header and the size of a memory node header. Then set the last node of the memory node as the last node of the memory pool OS_MEM_END_NODE(pool, size).

⑷ Call the macro to set the magic word for the node, and then insert the memory node into the free memory linked list. ⑸ Get the tail node of the memory pool, set the magic word, and then execute ⑹Set the tail node size to 0 and set the previous node, and set the used flag. If you enable the commissioning macro LOSCFG_MEM_WATERLINE, there will be some other operations, just read it yourself.

STATIC UINT32 OsMemPoolInit(VOID *pool, UINT32 size)
{
    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;
    struct OsMemNodeHead *newNode = NULL;
    struct OsMemNodeHead *endNode = NULL;

    (VOID)memset_s(poolHead, sizeof(struct OsMemPoolHead), 0, sizeof(struct OsMemPoolHead));

⑴  poolHead->info.pool = pool;
    poolHead->info.totalSize = size;
    poolHead->info.attr &= ~(OS_MEM_POOL_UNLOCK_ENABLE | OS_MEM_POOL_EXPAND_ENABLE); /* default attr: lock, not expand. */

⑶  newNode = OS_MEM_FIRST_NODE(pool);
    newNode->sizeAndFlag = (size - sizeof(struct OsMemPoolHead) - OS_MEM_NODE_HEAD_SIZE);
    newNode->ptr.prev = OS_MEM_END_NODE(pool, size);
⑷  OS_MEM_SET_MAGIC(newNode);
    OsMemFreeNodeAdd(pool, (struct OsMemFreeNodeHead *)newNode);

    /* The last mem node */
⑸  endNode = OS_MEM_END_NODE(pool, size);
    OS_MEM_SET_MAGIC(endNode);
#if OS_MEM_EXPAND_ENABLE
    endNode->ptr.next = NULL;
    OsMemSentinelNodeSet(endNode, NULL, 0);
#else
⑹  endNode->sizeAndFlag = 0;
    endNode->ptr.prev = newNode;
    OS_MEM_NODE_SET_USED_FLAG(endNode->sizeAndFlag);
#endif
#if (LOSCFG_MEM_WATERLINE == 1)
    poolHead->info.curUsedSize = sizeof(struct OsMemPoolHead) + OS_MEM_NODE_HEAD_SIZE;
    poolHead->info.waterLine = poolHead->info.curUsedSize;
#endif

    return LOS_OK;
}

2.3 Apply for dynamic memory

After initializing the dynamic memory pool, we can use the function VOID LOS_MemAlloc (VOID pool, UINT32 size) to apply for dynamic memory. The source code is analyzed below.

⑴ Check the parameters, the memory pool address cannot be empty, and the requested memory size cannot be 0. (2) It is judged whether the requested memory size has been marked as used or memory aligned. (3) Call the function OsMemAlloc(poolHead, size, intSave) to apply for a memory block.

VOID *LOS_MemAlloc(VOID *pool, UINT32 size)
{
#if OS_MEM_TRACE
    UINT64 start = HalClockGetCycles();
#endif

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

    if (size < OS_MEM_MIN_ALLOC_SIZE) {
        size = OS_MEM_MIN_ALLOC_SIZE;
    }

    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;
    VOID *ptr = NULL;
    UINT32 intSave;

    MEM_LOCK(poolHead, intSave);
    do {
⑵      if (OS_MEM_NODE_GET_USED_FLAG(size) || OS_MEM_NODE_GET_ALIGNED_FLAG(size)) {
            break;
        }
⑶      ptr = OsMemAlloc(poolHead, size, intSave);
    } while (0);
    MEM_UNLOCK(poolHead, intSave);

#if OS_MEM_TRACE
    UINT64 end = HalClockGetCycles();
    UINT32 timeUsed = MEM_TRACE_CYCLE_TO_US(end - start);
    LOS_Trace(LOS_TRACE_MEM_TIME, (UINTPTR)pool & MEM_POOL_ADDR_MASK, MEM_TRACE_MALLOC, timeUsed);

    LOS_MEM_POOL_STATUS poolStatus = {0};
    (VOID)LOS_MemInfoGet(pool, &poolStatus);
    UINT8 fragment = 100 - poolStatus.maxFreeNodeSize * 100 / poolStatus.totalFreeSize; /* 100: percent denominator. */
    UINT8 usage = LOS_MemTotalUsedGet(pool) * 100 / LOS_MemPoolSizeGet(pool); /* 100: percent denominator. */
    LOS_Trace(LOS_TRACE_MEM_INFO, (UINTPTR)pool & MEM_POOL_ADDR_MASK, fragment, usage, poolStatus.totalFreeSize,
              poolStatus.maxFreeNodeSize, poolStatus.usedNodeNum, poolStatus.freeNodeNum);
#endif

    OsHookCall(LOS_HOOK_TYPE_MEM_ALLOC, pool, size);

    return ptr;
}

We continue to analyze the function OsMemAlloc(). (1) Perform memory alignment on the sum of the application memory size plus the head node size. (2) Obtain a free memory block that meets the application size from the free memory linked list. If the application fails, an error message will be printed. (3) If the found memory block is larger than the required memory size, the segmentation operation is performed. (4) Mark the allocated memory node as used, and update the waterline record. ⑸Return the address of the data area of the memory block. This is achieved by adding 1 to the memory node address to locate the memory address of the data area. The memory application is completed, and the memory requested can be used in the function that calls the memory application.

STATIC INLINE VOID *OsMemAlloc(struct OsMemPoolHead *pool, UINT32 size, UINT32 intSave)
{
    struct OsMemNodeHead *allocNode = NULL;

#if (LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK == 1)
    if (OsMemAllocCheck(pool, intSave) == LOS_NOK) {
        return NULL;
    }
#endif

⑴  UINT32 allocSize = OS_MEM_ALIGN(size + OS_MEM_NODE_HEAD_SIZE, OS_MEM_ALIGN_SIZE);

#if OS_MEM_EXPAND_ENABLE
retry:
#endif
⑵  allocNode = OsMemFreeNodeGet(pool, allocSize);
    if (allocNode == NULL) {
#if OS_MEM_EXPAND_ENABLE
        if (pool->info.attr & OS_MEM_POOL_EXPAND_ENABLE) {
            INT32 ret = OsMemPoolExpand(pool, allocSize, intSave);
            if (ret == 0) {
                goto retry;
            }
        }
#endif
        PRINT_ERR("---------------------------------------------------"
                  "--------------------------------------------------------\n");
        MEM_UNLOCK(pool, intSave);
        OsMemInfoPrint(pool);
        MEM_LOCK(pool, intSave);
        PRINT_ERR("[%s] No suitable free block, require free node size: 0x%x\n", __FUNCTION__, allocSize);
        PRINT_ERR("----------------------------------------------------"
                  "-------------------------------------------------------\n");
        return NULL;
    }

⑶  if ((allocSize + OS_MEM_MIN_LEFT_SIZE) <= allocNode->sizeAndFlag) {
        OsMemSplitNode(pool, allocNode, allocSize);
    }

⑷  OS_MEM_NODE_SET_USED_FLAG(allocNode->sizeAndFlag);
    OsMemWaterUsedRecord(pool, OS_MEM_NODE_GET_SIZE(allocNode->sizeAndFlag));

#if (LOSCFG_MEM_LEAKCHECK == 1)
    OsMemLinkRegisterRecord(allocNode);
#endif
⑸  return OsMemCreateUsedNode((VOID *)allocNode);
}

2.4 Apply for dynamic memory according to the specified byte alignment

We can also use the function VOID LOS_MemAllocAlign (VOID pool, UINT32 size, UINT32 boundary) to apply for a memory with a length of size and an address aligned by boundary bytes from the specified dynamic memory pool. This function requires 3 parameters, VOID pool is the starting address of the memory pool, UINT32 size is the memory size to be applied for, and the UINT32 boundary memory alignment value. ptr obtained after applying for memory, the aligned memory address is VOID *alignedPtr, and the offset value of the two is saved using UINT32 gapSize. Because the memory has been aligned by OS_MEM_ALIGN_SIZE, the maximum offset value is boundary-OS_MEM_ALIGN_SIZE. The source code is analyzed below.

⑴ Check the parameters, the memory pool address cannot be empty, the requested memory size cannot be 0, the alignment byte boundary cannot be 0, and it needs to be a power of 2. The requested memory size must be greater than the minimum requested value OS_MEM_MIN_ALLOC_SIZE. (2) Check whether there will be data overflow after the memory is aligned. (3) Calculate the memory size that needs to be applied for after alignment, and then determine that the memory size value has not been used or has been aligned. (4) Call the function to apply for the memory VOID ptr, and then calculate the aligned memory address VOID alignedPtr, and return if the two are equal. ⑸ Calculate the offset value of the aligned memory, ⑹ get the head node of the applied memory, and set the aligned mark. ⑺ Set the alignment mark for the offset value, and then save the offset value in the first 4 bytes of the memory VOID *alignedPtr. ⑻ Re-orient the pointer to be returned to complete the memory application for alignment.

VOID *LOS_MemAllocAlign(VOID *pool, UINT32 size, UINT32 boundary)
{
#if OS_MEM_TRACE
    UINT64 start = HalClockGetCycles();
#endif

    UINT32 gapSize;

⑴  if ((pool == NULL) || (size == 0) || (boundary == 0) || !OS_MEM_IS_POW_TWO(boundary) ||
        !OS_MEM_IS_ALIGNED(boundary, sizeof(VOID *))) {
        return NULL;
    }

    if (size < OS_MEM_MIN_ALLOC_SIZE) {
        size = OS_MEM_MIN_ALLOC_SIZE;
    }

⑵  if ((boundary - sizeof(gapSize)) > ((UINT32)(-1) - size)) {
        return NULL;
    }

⑶  UINT32 useSize = (size + boundary) - sizeof(gapSize);
    if (OS_MEM_NODE_GET_USED_FLAG(useSize) || OS_MEM_NODE_GET_ALIGNED_FLAG(useSize)) {
        return NULL;
    }

    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;
    UINT32 intSave;
    VOID *ptr = NULL;
    VOID *alignedPtr = NULL;

    MEM_LOCK(poolHead, intSave);
    do {
⑷      ptr = OsMemAlloc(pool, useSize, intSave);
        alignedPtr = (VOID *)OS_MEM_ALIGN(ptr, boundary);
        if (ptr == alignedPtr) {
            break;
        }

        /* store gapSize in address (ptr - 4), it will be checked while free */
⑸      gapSize = (UINT32)((UINTPTR)alignedPtr - (UINTPTR)ptr);
⑹      struct OsMemUsedNodeHead *allocNode = (struct OsMemUsedNodeHead *)ptr - 1;
        OS_MEM_NODE_SET_ALIGNED_FLAG(allocNode->header.sizeAndFlag);
⑺      OS_MEM_SET_GAPSIZE_ALIGNED_FLAG(gapSize);
        *(UINT32 *)((UINTPTR)alignedPtr - sizeof(gapSize)) = gapSize;
⑻      ptr = alignedPtr;
    } while (0);
    MEM_UNLOCK(poolHead, intSave);

#if OS_MEM_TRACE
    UINT64 end = HalClockGetCycles();
    UINT32 timeUsed = MEM_TRACE_CYCLE_TO_US(end - start);
    LOS_Trace(LOS_TRACE_MEM_TIME, (UINTPTR)pool & MEM_POOL_ADDR_MASK, MEM_TRACE_MEMALIGN, timeUsed);
#endif

    OsHookCall(LOS_HOOK_TYPE_MEM_ALLOCALIGN, pool, size, boundary);

    return ptr;
}

2.5 Release dynamic memory

After using the requested memory block, we can use the function UINT32 LOS_MemFree (VOID pool, VOID ptr) to release the dynamic memory. Two parameters are required. VOID pool is the initialized dynamic memory pool address. VOID ptr is the starting address of the data area of the dynamic memory block that needs to be released. Note that this is not the address of the memory control node. Next, analyze the source code, and verify the incoming parameters first. (2) Obtain the real memory address after the alignment of the calibrated memory, and then obtain the memory node head address. (3) Call the function OsMemFree (pool, ptr) to complete the release of the memory.

UINT32 LOS_MemFree(VOID *pool, VOID *ptr)
{
#if OS_MEM_TRACE
    UINT64 start = HalClockGetCycles();
#endif

⑴  if ((pool == NULL) || (ptr == NULL) || !OS_MEM_IS_ALIGNED(pool, sizeof(VOID *)) ||
        !OS_MEM_IS_ALIGNED(ptr, sizeof(VOID *))) {
        return LOS_NOK;
    }

    UINT32 ret = LOS_NOK;
    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;
    struct OsMemNodeHead *node = NULL;
    UINT32 intSave;

    MEM_LOCK(poolHead, intSave);
    do {
⑵      ptr = OsGetRealPtr(pool, ptr);
        if (ptr == NULL) {
            break;
        }
        node = (struct OsMemNodeHead *)((UINTPTR)ptr - OS_MEM_NODE_HEAD_SIZE);
⑶      ret = OsMemFree(poolHead, node);
    } while (0);
    MEM_UNLOCK(poolHead, intSave);

#if OS_MEM_TRACE
    UINT64 end = HalClockGetCycles();
    UINT32 timeUsed = MEM_TRACE_CYCLE_TO_US(end - start);
    LOS_Trace(LOS_TRACE_MEM_TIME, (UINTPTR)pool & MEM_POOL_ADDR_MASK, MEM_TRACE_FREE, timeUsed);
#endif

    OsHookCall(LOS_HOOK_TYPE_MEM_FREE, pool, ptr);

    return ret;
}

Let's go back and continue to look at the function OsGetRealPtr(). (1) Get the offset value of memory alignment, (2) If the offset value is marked as used and aligned at the same time, an error will be returned. (3) If the offset value is marked as aligned, execute ⑷ to remove the alignment mark and obtain the offset value without the mark. Then execute ⑸ to obtain the memory address of the data area before memory alignment.

STATIC INLINE VOID *OsGetRealPtr(const VOID *pool, VOID *ptr)
{
    VOID *realPtr = ptr;
⑴  UINT32 gapSize = *((UINT32 *)((UINTPTR)ptr - sizeof(UINT32)));

⑵  if (OS_MEM_GAPSIZE_CHECK(gapSize)) {
        PRINT_ERR("[%s:%d]gapSize:0x%x error\n", __FUNCTION__, __LINE__, gapSize);
        return NULL;
    }

⑶  if (OS_MEM_GET_GAPSIZE_ALIGNED_FLAG(gapSize)) {
⑷      gapSize = OS_MEM_GET_ALIGNED_GAPSIZE(gapSize);
        if ((gapSize & (OS_MEM_ALIGN_SIZE - 1)) ||
            (gapSize > ((UINTPTR)ptr - OS_MEM_NODE_HEAD_SIZE - (UINTPTR)pool))) {
            PRINT_ERR("[%s:%d]gapSize:0x%x error\n", __FUNCTION__, __LINE__, gapSize);
            return NULL;
        }
⑸      realPtr = (VOID *)((UINTPTR)ptr - (UINTPTR)gapSize);
    }
    return realPtr;
}

2.6 Re-apply for dynamic memory

We can also use the function VOID LOS_MemRealloc(VOID pool, VOID ptr, UINT32 size) to reallocate the memory block according to the specified size, and copy the contents of the original memory block to the new memory block. If the application for the new memory block is successful, the original memory block is released. This function requires 3 parameters, VOID pool is the starting address of the memory pool, VOID *ptr is the memory address previously applied for, and UINT32 size is the re-applied memory size. The return value is the address of the new memory block, or NULL is returned. The source code is analyzed below.

⑴ Check the parameters, the memory pool address cannot be empty, and the memory size cannot contain used and aligned marks. (2) If the memory address passed in is empty, it is equivalent to the LOS_MemAlloc() function. (3) If the incoming size is 0, it is equivalent to the function LOS_MemFree(). (4) Ensure that the requested memory block size is at least the minimum value OS_MEM_MIN_ALLOC_SIZE allowed by the system. (5) Get the memory address before the memory alignment. The function OsGetRealPtr() has been analyzed above. ⑹The memory address of the memory control node node is calculated from the memory address of the data domain, and then the function ⑺ is executed to reapply for memory.

VOID *LOS_MemRealloc(VOID *pool, VOID *ptr, UINT32 size)
{
#if OS_MEM_TRACE
    UINT64 start = HalClockGetCycles();
#endif

⑴  if ((pool == NULL) || OS_MEM_NODE_GET_USED_FLAG(size) || OS_MEM_NODE_GET_ALIGNED_FLAG(size)) {
        return NULL;
    }

    OsHookCall(LOS_HOOK_TYPE_MEM_REALLOC, pool, ptr, size);

⑵  if (ptr == NULL) {
        return LOS_MemAlloc(pool, size);
    }

⑶  if (size == 0) {
        (VOID)LOS_MemFree(pool, ptr);
        return NULL;
    }

⑷  if (size < OS_MEM_MIN_ALLOC_SIZE) {
        size = OS_MEM_MIN_ALLOC_SIZE;
    }

    struct OsMemPoolHead *poolHead = (struct OsMemPoolHead *)pool;
    struct OsMemNodeHead *node = NULL;
    VOID *newPtr = NULL;
    UINT32 intSave;

    MEM_LOCK(poolHead, intSave);
    do {
⑸      ptr = OsGetRealPtr(pool, ptr);
        if (ptr == NULL) {
            break;
        }

⑹      node = (struct OsMemNodeHead *)((UINTPTR)ptr - OS_MEM_NODE_HEAD_SIZE);
        if (OsMemCheckUsedNode(pool, node) != LOS_OK) {
            break;
        }

⑺      newPtr = OsMemRealloc(pool, ptr, node, size, intSave);
    } while (0);
    MEM_UNLOCK(poolHead, intSave);

#if OS_MEM_TRACE
    UINT64 end = HalClockGetCycles();
    UINT32 timeUsed = MEM_TRACE_CYCLE_TO_US(end - start);
    LOS_Trace(LOS_TRACE_MEM_TIME, (UINTPTR)pool & MEM_POOL_ADDR_MASK, MEM_TRACE_REALLOC, timeUsed);
#endif

    return newPtr;
}

Continue to analyze the function OsMemRealloc. ⑴ In case the re-applied memory is less than or equal to the existing memory, you need to call the function OsMemReAllocSmaller() for segmentation, and then return (VOID *)ptr after segmentation. If you re-apply for a larger memory, execute the code at (2) to get the next node, and then execute ⑶ to process the next node is available and the sum of the two node sizes is greater than or equal to the re-applied memory size allocSize. Execute the function at ⑷, merge the node to reallocate the memory.

If the size of consecutive nodes does not meet the size of re-applying memory, execute function (5) to re-apply for memory. After the application is successful, execute ⑹ to copy the data in the previous memory to the newly applied memory area. If the copy fails, the newly applied memory is released, and NULL is returned, and the function is exited. If the copy is successful, continue to execute ⑺ to release the previous node.

STATIC INLINE VOID *OsMemRealloc(struct OsMemPoolHead *pool, const VOID *ptr,
                struct OsMemNodeHead *node, UINT32 size, UINT32 intSave)
{
    struct OsMemNodeHead *nextNode = NULL;
    UINT32 allocSize = OS_MEM_ALIGN(size + OS_MEM_NODE_HEAD_SIZE, OS_MEM_ALIGN_SIZE);
    UINT32 nodeSize = OS_MEM_NODE_GET_SIZE(node->sizeAndFlag);
    VOID *tmpPtr = NULL;

⑴  if (nodeSize >= allocSize) {
        OsMemReAllocSmaller(pool, allocSize, node, nodeSize);
        return (VOID *)ptr;
    }

⑵  nextNode = OS_MEM_NEXT_NODE(node);
⑶  if (!OS_MEM_NODE_GET_USED_FLAG(nextNode->sizeAndFlag) &&
        ((nextNode->sizeAndFlag + nodeSize) >= allocSize)) {
⑷      OsMemMergeNodeForReAllocBigger(pool, allocSize, node, nodeSize, nextNode);
        return (VOID *)ptr;
    }

⑸  tmpPtr = OsMemAlloc(pool, size, intSave);
    if (tmpPtr != NULL) {
⑹      if (memcpy_s(tmpPtr, size, ptr, (nodeSize - OS_MEM_NODE_HEAD_SIZE)) != EOK) {
            MEM_UNLOCK(pool, intSave);
            (VOID)LOS_MemFree((VOID *)pool, (VOID *)tmpPtr);
            MEM_LOCK(pool, intSave);
            return NULL;
        }
⑺      (VOID)OsMemFree(pool, node);
    }
    return tmpPtr;
}

summary

This article led everyone to analyze the source code of the static memory module of the Hongmeng Light Kernel, including the structure of the dynamic memory, the initialization of the dynamic memory pool, the application and release of the dynamic memory, and so on. Thanks for reading. If you have any questions or suggestions, you can leave a message to us: https://gitee.com/openharmony/kernel_liteos_m/issues . light kernel code warehouse easier, it is recommended to visit 160ebbe4a938a8 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 粉丝

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