目录
- 一、动态内存运行机制
- 二、动态内存开发流程
- 三、动态内存使用说明
- 四、动态内存核心算法
- 五、动态内存接口
- 六、代码分析(待续...)
- 坚持就有收获
一、动态内存运行机制
动态内存管理,即在内存资源充足的情况下,根据用户需求,从系统配置的一块比较大的连续内存(内存池,也是堆内存)中分配任意大小的内存块。当用户不需要该内存块时,又可以释放回系统供下一次使用。
二、动态内存开发流程
- 初始化 LOS_MemInit。初始一个内存池后生成一个内存池控制头、尾节点 EndNode,剩余的内存被标记为 FreeNode 内存节点。注:EndNode 作为内存池末尾的节点,size 为0。
- 申请任意大小的动态内存 LOS_MemAlloc。判断动态内存池中是否存在大于申请量大小的空闲内存块空间,若存在,则划出一块内存块,以指针形式返回,若不存在,返回NULL。如果空闲内存块大于申请量,需要对内存块进行分割,剩余的部分作为空闲内存
块挂载到空闲内存链表上。 - 释放动态内存 LOS_MemFree。回收内存块,供下一次使用。调用 LOS_MemFree 释放内存块,则会回收内存块,并且将其标记为 FreeNode。在回收内存块时,相邻的FreeNode 会自动合并。
三、动态内存使用说明
- 由于动态内存管理需要管理控制块数据结构来管理内存,这些数据结构会额外消耗内存,故实际用户可使用内存总量小于配置项 OS_SYS_MEM_SIZE 的大小。
- 对齐分配内存接口 LOS_MemAllocAlign/LOS_MemMallocAlign 因为要进行地址对齐,可能会额外消耗部分内存,故存在一些遗失内存,当系统释放该对齐内存时,同时回收由于对齐导致的遗失内存。
- 非连续性内存区域接口 LOS_MemRegionsAdd 的 LosMemRegion 数组参数传入的非连续性内存区域需要按各个内存区域的内存开始地址升序,且内存区域不能重叠。
四、动态内存核心算法
TLSF:Two-Level Segregated Fit 两级分割策略算法.
OpenHarmony LiteOS-M动态内存在TLSF算法的基础上,对区间的划分进行了优化,获得更优的性能,降低了碎片率。动态内存核心算法框图如下:
根据空闲内存块的大小,使用多个空闲链表来管理。根据内存空闲块大小分为两个部分:[4, 127]和[27, 231],如上图size class所示:
-
对[4,127]区间的内存进行等分,如上图绿色部分所示,分为31个小区间,每个小区间对应内存块大小为4字节的倍数。每个小区间对应一个空闲内存链表和用于标记对应空闲内存链表是否为空的一个比特位,值为1时,空闲链表非空。[4,127]区间的内存使用1个32位无符号整数位图标记。
-
大于127字节的空闲内存块,按照2的次幂区间大小进行空闲链表管理。总共分为24个小区间,每个小区间又等分为8个二级小区间,见上图蓝色的Size Class和Size SubClass部分。每个二级小区间对应一个空闲链表和用于标记对应空闲内存链表是否为空的一个比特位。总共24*8=192个二级小区间,对应192个空闲链表和192/32=6个32位无符号整数位图标记。
例如,当有40字节的空闲内存需要插入空闲链表时,对应小区间[40,43],第10个空闲链表,位图标记的第10比特位。把40字节的空闲内存挂载第10个空闲链表上,并判断是否需要更新位图标记。当需要申请40字节的内存时,根据位图标记获取存在满足申请大小的内存块的空闲链表,从空闲链表上获取空闲内存节点。如果分配的节点大于需要申请的内存大小,进行分割节点操作,剩余的节点重新挂载到相应的空闲链表上。
当有580字节的空闲内存需要插入空闲链表时,对应二级小区间[29,29+2^6],第31+2*8=47个空闲链表,第2个位图标记的第17比特位。把580字节的空闲内存挂载第47个空闲链表上,并判断是否需要更新位图标记。当需要申请580字节的内存时,根据位图标记获取存在满足申请大小的内存块的空闲链表,从空闲链表上获取空闲内存节点。如果分配的节点大于需要申请的内存大小,进行分割节点操作,剩余的节点重新挂载到相应的空闲链表上。如果对应的空闲链表为空,则向更大的内存区间去查询是否有满足条件的空闲链表,实际计算时,会一次性查找到满足申请大小的空闲链表。
五、动态内存接口
功能类别 | 接口函数 | 描述 |
---|---|---|
初始化和删除内存池 | LOS_MemInit | 初始化一块指定的动态内存池,大小为size |
LOS_MemDeInit | 删除指定内存池,仅打开LOSCFG_MEM_MUL_POOL时有效 | |
申请、释放动态内存 | LOS_MemAlloc | 从指定动态内存池中申请size长度的内存 |
LOS_MemFree | 释放已申请的内存 | |
LOS_MemRealloc | 按size大小重新分配内存块,并将原内存块内容拷贝到新内存块。如果新内存块申请成功,则释放原内存块 | |
LOS_MemAllocAlign | 从指定动态内存池中申请长度为size且地址按boundary字节对齐的内存 | |
获取内存池信息 | LOS_MemPoolSizeGet | 获取指定动态内存池的总大小 |
LOS_MemTotalUsedGet | 获取指定动态内存池的总使用量大小 | |
LOS_MemInfoGet | 获取指定内存池的内存结构信息,包括空闲内存大小、已使用内存大小、空闲内存块数量、已使用的内存块数量、最大的空闲内存块大小 | |
LOS_MemPoolList | 打印系统中已初始化的所有内存池,包括内存池的起始地址、内存池大小、空闲内存总大小、已使用内存总大小、最大的空闲内存块大小、空闲内存块数量、已使用的内存块数量。仅打开LOSCFG_MEM_MUL_POOL时有效 | |
获取内存块信息 | LOS_MemFreeBlksGet | 获取指定内存池的空闲内存块数量 |
LOS_MemUsedBlksGet | 获取指定内存池已使用的内存块数量 | |
LOS_MemTaskIdGet | 获取申请了指定内存块的任务ID | |
LOS_MemLastUsedGet | 获取内存池最后一个已使用内存块的结束地址 | |
LOS_MemNodeSizeCheck | 获取指定内存块的总大小和可用大小,仅打开LOSCFG_BASE_MEM_NODE_SIZE_CHECK时有效 | |
LOS_MemFreeNodeShow | 打印指定内存池的空闲内存块的大小及数量 | |
检查指定内存池的完整性 | LOS_MemIntegrityCheck | 对指定内存池做完整性检查,仅打开LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK时有效 |
设置、获取内存检查级别,仅打开LOSCFG_BASE_MEM_NODE_SIZE_CHECK时有效 | LOS_MemCheckLevelSet | 设置内存检查级别 |
LOS_MemCheckLevelGet | 获取内存检查级别 | |
为指定模块申请、释放动态内存,仅打开LOSCFG_MEM_MUL_MODULE时有效 | LOS_MemMalloc | 从指定动态内存池分配size长度的内存给指定模块,并纳入模块统计 |
LOS_MemMfree | 释放已经申请的内存块,并纳入模块统计 | |
LOS_MemMallocAlign | 从指定动态内存池中申请长度为size且地址按boundary字节对齐的内存给指定模块,并纳入模块统计 | |
LOS_MemMrealloc | 按size大小重新分配内存块给指定模块,并将原内存块内容拷贝到新内存块,同时纳入模块统计。如果新内存块申请成功,则释放原内存块 | |
获取指定模块的内存使用量 | LOS_MemMusedGet | 获取指定模块的内存使用量,仅打开LOSCFG_MEM_MUL_MODULE时有效 |