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指针,但相邻内存块是地址是有序的,因此通过当前内存块的起始地址+内存块的大小,也可以找到下一个相邻的内存块。
内存池的管理信息和内存块管理信息分布:在这里插入图片描述

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

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指针组成双向链表。
    在这里插入图片描述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来完成。它们的映射关系如下:
在这里插入图片描述freeListBitmap的每个bit都表示某个分组是否存在空闲内存块,以此来加快内存申请的速度。OsMemFreeNodeHead则是每个空闲内存分组的头节点,同一分组的所有空闲内存块信息都和头节点组成双向链表。如下图,内存分组4的管理内存大小是20—23字节的所有空闲内存块,而内存分组0管理内存大小是4—7字节的所有空闲内存块。
在这里插入图片描述

内存申请

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

分配内存的算法

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

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

内存释放

内存释放时首先需要检查内存的有效性,然后清除该内存块的使用标记,之后检查相邻的内存块是否空闲,如果是则合并成一个大块。最后将该内存块加入空闲内存块列表中。
在这里插入图片描述

内存使用接口

接口描述
LOS_MemInit初始化内存池
LOS_MemAlloc分配内存
LOS_MemFree释放内存
本课程《华为物联网操作系统LiteOS》是朱老师物联网大讲堂推出的一套物联网理论和实践相结合的视频课程。本课程以渐次递进的方式讲了以下4个主题。主题1:物联网,这是整个课程第1部分。主要讲了物联网的概念、发展历程、物联网的典型案例和应用场景,从技术角度深度阐述了物联网的4层架构、分析了各层次的核心技术和实现原理。本部分的主要目的是让大家对物联网有一定深度和专业性的理解。很多人一直对物联网有兴趣,也找了不少资料看了不少书,但是越看越糊涂,尤其很多物联网专业的大学生,经过几年的大学学习仍然不知道究竟什么是物联网,更不知该如何去学习物联网,本部分就是为解决这个疑问而生。主题2:操作系统,这是整个课程第2部分。主要讲了操作系统的基本原理,操作系统的作用和组成部分,让我们明白裸机开发和基于操作系统的开发有什么差异。这部分是比较偏理论的,是为了解决很多同学对操作系统的认知基础的。很多同学甚至是开发者,尤其是单片机的开发者习惯了裸机开发,直接基于寄存器或者官方库函数(譬如stm32的HAL库、标准库)的开发,心里很疑惑到底什么是操作系统,为什么裸机开发也能做项目还需要操作系统?用不用操作系统的差异在哪里?为什么要去学习操作系统?应该如何学习操作系统?本部分就是为了回答这些问题。主题3:物联网操作系统,这是整个课程的的3部分。物联网操作系统是专为物联网而研发和设计的操作系统,是物联网设备的核心技术。物联网操作系统也是一种操作系统,他具有操作系统的普遍特性(以前前面我们才先学习泛性的普遍的操作系统),但是物联网操作系统有它很多独特的特性,华为的LiteOS就是一款非常典型的优秀的物联网操作系统,除此之外国内还有诸如RT-Thread、AliOSThings等其他优秀物联网操作系统,国外还有Amazon的Freertos等物联网操作系统。那究竟物联网操作系统有什么特别之处?物联网产品如何选择操作系统?如何基于操作系统来开发物联网产品?本部分课程将回答这些问题。主题4:华为物联网操作系统LiteOS,这是整个课程的第4部分。本部分聚焦LiteOS,基于前3部分的铺垫,向大家详细讲解LiteOS的设计思路,专门安排了2大章节来详细分析LiteOS的kernel源码和周边组件源码,还介绍了我们专为学习物联网而设计的NB476开发板,且基于该开发板和LiteOS设计了一个温湿度和断电检测报警器的典型的物联网产品试验,在试验实战中让大家体会基于LiteOS物联网项目的开发方式。本部分是整个课程中最重头戏的部分,篇幅占据整个课程的一半左右。因此实际上我们整个课程的内容还是比较偏技术性的,可谓低走高开。从基础概念起步,最终带大家能够去做产品。课程特色*完全零基础,降低学习门槛。*深入浅出,通俗易懂。不怕学不会,就怕你不学习。*思路清晰、语言风趣,对着视频看也不会想睡觉······*视频 + 文档 + 练习题 + 答疑,全方位保证学习质量。*基础知识 + 思路引导的教学方式,授之以鱼更授之以渔。*系列课程。本教程只是入门篇,后续还有更多更精彩视频更新中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值