LiteOS-M动态内存管理

LiteOS-M动态内存管理

  • LiteOS-M动态内存管理
    • 内存池初始化流程
    • 内存管理信息
      • 内存池管理信息结构和解析
      • 内存块信息结构和解析
    • 动态内存管理原理
      • 内存申请
        • 分配内存的算法
      • 内存释放
    • 内存使用接口

LiteOS-M动态内存管理

LiteOS-M的动态内存管理将大块的预分配内存,通过最佳适应算法,动态分配给申请者。动态内存分配算法都无法避免内存碎片化,但通过最佳适应算法能够最大限度的减少内存碎片化。

代码实现文件:

kernel/liteos_m/kernel/mm/los_memory.c
kernel/liteos_m/kernel/include/los_memory.h

内存池初始化流程

LiteOS-M的内存池的初始化通过调用函数OsMemPoolInit()来完成。

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);  //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;    //内存块size为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;
}
  1. 首先将内存池的顶部作为内存池的管理信息,设置内存池的起始地址、大小和缺省属性;
  2. 然后将剩余的内存作为一个空闲内存块,并加入空闲内存列表中;
  3. 最后在内存池底部设置结束内存块(内存块size等于0、且标记为已使用);

内存管理信息

一个内存池是通过内存池管理信息和内存块信息来共同管理的。内存池管理信息在内存池的头部,内存块信息则在每个空闲或已分配内存的头部。用户申请内存,实际分配的内存空间还包含了内存块信息,只是这部分对用户不可见。
所有内存块信息通过prev指针组成单向循环链表,且相邻内存块的地址是有序的。通过prev指针能够找到前一个内存块的地址;由于没有使用next指针,但相邻内存块是地址是有序的,因此通过当前内存块的起始地址+内存块的大小,也可以找到下一个相邻的内存块。
内存池的管理信息和内存块管理信息分布:LiteOS-M动态内存管理_第1张图片

内存池管理信息结构和解析

struct OsMemPoolInfo {
    VOID *pool;
    UINT32 totalSize;
    UINT32 attr;
#if (LOSCFG_MEM_WATERLINE == 1)
    UINT32 waterLine;   /* Maximum usage size in a memory pool */
    UINT32 curUsedSize; /* Current usage size in a memory pool */
#endif
};

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
};
  • OsMemPoolInfo:内存池信息
    • pool:内存池地址
    • totalSize:内存池大小
    • attr:内存管理扩展属性
    • waterLine:记录内存消耗水位
    • curUsedSize:记录当前消耗的内存
  • freeListBitmap:空闲内存块位图,标记所对应的空闲内存块是否存在
    freeListBitmap数组成员是32位整数,数组成员数量=空闲块数组数量/32+1(223/32+1=7),数组成员的每个bit对应一个OsMemFreeNodeHead数组成员,以此来标识空闲内存块的状态。
  • OsMemFreeNodeHead:空闲内存块的数组
    OsMemFreeNodeHead用于管理空闲内存,每个数组成员对应一个内存分组。数组成员共有31+24*8=223个,其中下标为0-30的31个数组成员管理128字节以内的小空闲内存块,每4字节为一个内存分组;另外下标为31-222的192个数组成员管理超过或等于128字节的空闲内存块,划分方法是通过2的n次幂(n=7~31)确定空闲内存块的一级内存分组边界,每个一级内存分组又分成8等份形成大空闲内存块使用的二级内存分组(如下图)。同一分组的空闲内存块通过prev和next指针组成双向链表。
    LiteOS-M动态内存管理_第2张图片OsMemFreeNodeHead数组成员:
    • header:内存块信息
    • prev:前一个空闲内存块的指针
    • next:下一个空闲内存块的指针
  • nextPool:当支持多内存池配置时,指向下一个内存池

内存块信息结构和解析

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
};
  • magic:用于内存完整性检查的魔数
  • linkReg:用于内存泄漏检测的队列信息
  • prev:指向前一个内存块指针
  • next:指向下一个内存块指针
  • taskID:分配内存的进程ID,如果开启了内存完整性检测功能
  • sizeAndFlag:0-21位是内存块大小;22-31位是内存标识,如已分配标记、对齐标记、内存泄露标记等

动态内存管理原理

LiteOS-M动态内存管理系统通过分组来管理内存的,如上节所述,会将所有内存分成223个分组,每个分组负责管理特定大小的空闲内存。我们知道,在内存池初始化时会将所有剩余内存转换成一个很大的空闲内存块,而这个空闲内存块就会像一个大蛋糕一样,一点点被分割出去。申请时切割后的空闲内存块会按大小重新分组,释放时也会将回收内存块按大小进行分组。
空闲内存块分组的管理是通过内存池管理信息的2个成员freeListBitmap和OsMemFreeNodeHead来完成。它们的映射关系如下:
LiteOS-M动态内存管理_第3张图片freeListBitmap的每个bit都表示某个分组是否存在空闲内存块,以此来加快内存申请的速度。OsMemFreeNodeHead则是每个空闲内存分组的头节点,同一分组的所有空闲内存块信息都和头节点组成双向链表。如下图,内存分组4的管理内存大小是20—23字节的所有空闲内存块,而内存分组0管理内存大小是4—7字节的所有空闲内存块。
LiteOS-M动态内存管理_第4张图片

内存申请

内存在申请时,如果开启了内存完整性检测功能,会首先检测所有的内存块,确定没有异常才会继续申请内存。然后通过最佳适应算法查找空闲内存块,如果找到的空闲内存块过大,还需要将内存块分割成2部分,一个和申请的内存一样大小,另外一个重新回到空闲内存分组列表中。最后申请到的空闲内存块需要做好已使用的标记,并返回申请的内存地址。
LiteOS-M动态内存管理_第5张图片

分配内存的算法

LiteOS-M采用的是最佳适应法,具体的方法如下:

  1. 首先依据申请内存的大小计算出所属空闲内存组;
  2. 然后在更大一级分组(内存大小肯定要比申请的大)查找是否存在空闲内存块,如果有则在该分组找最小的分配出去;
  3. 如果第二步没有找到,则从小到大开始检索后面所有的内存分组,直到找到空闲内存块;
  4. 在以上方法都没有找到的情况下,就会在所属内存分组中查找是否存在合适的空闲内存块。(为何不首先在所属内存分组内查找呢?

内存释放

内存释放时首先需要检查内存的有效性,然后清除该内存块的使用标记,之后检查相邻的内存块是否空闲,如果是则合并成一个大块。最后将该内存块加入空闲内存块列表中。
LiteOS-M动态内存管理_第6张图片

内存使用接口

接口 描述
LOS_MemInit 初始化内存池
LOS_MemAlloc 分配内存
LOS_MemFree 释放内存

你可能感兴趣的:(物联网,harmonyos,iot,c语言)