内存管理模块管理系统的内存资源,它是操作系统的核心模块之一,主要包括内存的初始化、分配以及释放。
在系统运行过程中,内存管理模块通过对内存的申请/释放来管理用户和OS对内存的使用,使内存的利用率和使用效率达到最优,同时最大限度地解决系统的内存碎片问题。
OpenHarmony LiteOS-M的内存管理分为静态内存管理和动态内存管理,提供内存初始化、分配、释放等功能。
静态内存实质上是一个静态数组,静态内存池内的块大小在初始化时设定,初始化后块大小不可变更。
静态内存池由一个控制块LOS_MEMBOX_INFO和若干相同大小的内存块LOS_MEMBOX_NODE构成。控制块位于内存池头部,用于内存块管理,包含内存块大小uwBlkSize,内存块数量uwBlkNum,已分配使用的内存块数量uwBlkCnt和空闲内存块链表stFreeList。内存块的申请和释放以块大小为粒度,每个内存块包含指向下一个内存块的指针pstNext。
图1 静态内存示意图
当用户需要使用固定长度的内存时,可以通过静态内存分配的方式获取内存,一旦使用完毕,通过静态内存释放函数归还所占用内存,使之可以重复使用。
OpenHarmony LiteOS-M的静态内存管理主要为用户提供以下功能,接口详细信息可以查看API参考。
表1 静态内存模块接口
功能分类 | 接口名 |
---|---|
初始化静态内存池 | LOS_MemboxInit:初始化一个静态内存池,根据入参设定其起始地址、总大小及每个内存块大小。 |
清除静态内存块内容 | LOS_MemboxClr:清零从静态内存池中申请的静态内存块的内容。 |
申请、释放静态内存 | LOS_MemboxAlloc:从指定的静态内存池中申请一块静态内存块。 LOS_MemboxFree:释放从静态内存池中申请的一块静态内存块。 |
获取、打印静态内存池信息 | LOS_MemboxStatisticsGet:获取指定静态内存池的信息,包括内存池中总内存块数量、已经分配出去的内存块数量、每个内存块的大小。 |
说明: 初始化后的内存池的内存块数量,不等于总大小除于内存块大小,因为内存池的控制块和每个内存块的控制头,都存在内存开销,设置总大小时,需要将这些因素考虑进去。
本节介绍使用静态内存的典型场景开发流程。
本实例执行以下步骤:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleStaticMem。
#include "los_membox.h"
#define MEMBOX_POOL_SIZE 100
#define MEMBOX_BLOCK_SZIE 10
#define MEMBOX_WR_TEST_NUM 828
VOID ExampleStaticMem(VOID)
{
UINT32 *mem = NULL;
UINT32 blkSize = MEMBOX_BLOCK_SZIE;
UINT32 poolSize = MEMBOX_POOL_SIZE;
UINT32 boxMem[MEMBOX_POOL_SIZE];
UINT32 ret;
/* 内存池初始化 */
ret = LOS_MemboxInit(&boxMem[0], poolSize, blkSize);
if(ret != LOS_OK) {
printf("Membox init failed!\n");
return;
} else {
printf("Membox init success!\n");
}
/* 申请内存块 */
mem = (UINT32 *)LOS_MemboxAlloc(boxMem);
if (mem == NULL) {
printf("Mem alloc failed!\n");
return;
}
printf("Mem alloc success!\n");
/* 内存地址读写验证 */
*mem = MEMBOX_WR_TEST_NUM;
printf("*mem = %d\n", *mem);
/* 清除内存内容 */
LOS_MemboxClr(boxMem, mem);
printf("Mem clear success \n*mem = %d\n", *mem);
/* 释放内存 */
ret = LOS_MemboxFree(boxMem, mem);
if (LOS_OK == ret) {
printf("Mem free success!\n");
} else {
printf("Mem free failed!\n");
}
return;
}
结果验证
输出结果如下:
Membox init success!
Mem alloc success!
*mem = 828
Mem clear success
*mem = 0
Mem free success!
运行机制
动态内存管理,即在内存资源充足的情况下,根据用户需求,从系统配置的一块比较大的连续内存(内存池,也是堆内存)中分配任意大小的内存块。当用户不需要该内存块时,又可以释放回系统供下一次使用。与静态内存相比,动态内存管理的优点是按需分配,缺点是内存池中容易出现碎片。
OpenHarmony LiteOS-M动态内存在TLSF算法的基础上,对区间的划分进行了优化,获得更优的性能,降低了碎片率。动态内存核心算法框图如下:
图1 轻量系统动态内存核心算法
根据空闲内存块的大小,使用多个空闲链表来管理。根据内存空闲块大小分为两个部分:[4, 127]和[27, 231],如上图size class所示:
例如,当有40字节的空闲内存需要插入空闲链表时,对应小区间[40,43],第10个空闲链表,位图标记的第10比特位。把40字节的空闲内存挂载第10个空闲链表上,并判断是否需要更新位图标记。当需要申请40字节的内存时,根据位图标记获取存在满足申请大小的内存块的空闲链表,从空闲链表上获取空闲内存节点。如果分配的节点大于需要申请的内存大小,进行分割节点操作,剩余的节点重新挂载到相应的空闲链表上。当有580字节的空闲内存需要插入空闲链表时,对应二级小区间[29,29+2^6],第31+2*8=47个空闲链表,并使用位图的第47个比特位来标记链表是否为空。把580字节的空闲内存挂载第47个空闲链表上,并判断是否需要更新位图标记。当需要申请580字节的内存时,根据位图标记获取存在满足申请大小的内存块的空闲链表,从空闲链表上获取空闲内存节点。如果分配的节点大于需要申请的内存大小,进行分割节点操作,剩余的节点重新挂载到相应的空闲链表上。如果对应的空闲链表为空,则向更大的内存区间去查询是否有满足条件的空闲链表,实际计算时,会一次性查找到满足申请大小的空闲链表。
内存管理结构如下图所示:
图2 轻量系统动态内存管理结构图
一些芯片片内RAM大小无法满足要求,需要使用片外物理内存进行扩充。对于这样的多段非连续性内存, LiteOS-M内核支持把多个非连续性内存逻辑上合一,用户不感知底层的多段非连续性内存区域。 LiteOS-M内核内存模块把不连续的内存区域作为空闲内存结点插入到空闲内存节点链表,把不同内存区域间的不连续部分标记为虚拟的已使用内存节点,从逻辑上把多个非连续性内存区域实现为一个统一的内存池。下面通过示意图说明下多段非连续性内存的运行机制:
图3 非连续性内存合一示意图
结合上述示意图,非连续性内存合并为一个统一的内存池的步骤如下:
动态内存管理的主要工作是动态分配并管理用户申请到的内存区间。动态内存管理主要用于用户需要使用大小不等的内存块的场景,当用户需要使用内存时,可以通过操作系统的动态内存申请函数索取指定大小的内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用。
OpenHarmony LiteOS-M的动态内存管理主要为用户提供以下功能,接口详细信息可以查看API参考。
表1 动态内存模块接口
功能分类 | 接口描述 |
---|---|
初始化和删除内存池 | LOS_MemInit:初始化一块指定的动态内存池,大小为size。 LOS_MemDeInit:删除指定内存池,仅打开编译控制开关LOSCFG_MEM_MUL_POOL时有效。 |
申请、释放动态内存 | LOS_MemAlloc:从指定动态内存池中申请size长度的内存。 LOS_MemFree:释放从指定动态内存中申请的内存。 LOS_MemRealloc:释放从指定动态内存中申请的内存。 |
获取内存池信息 | LOS_MemPoolSizeGet:获取指定动态内存池的总大小。 |
获取内存块信息 | LOS_MemFreeNodeShow:打印指定内存池的空闲内存块的大小及数量。 LOS_MemUsedNodeShow:打印指定内存池的已使用内存块的大小及数量。 |
检查指定内存池的完整性 | LOS_MemIntegrityCheck:对指定内存池做完整性检查,仅打开编译控制开关LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK时有效。 |
增加非连续性内存区域 | LOS_MemRegionsAdd:支持多段非连续性内存区域,把非连续性内存区域逻辑上整合为一个统一的内存池。仅打开LOSCFG_MEM_MUL_REGIONS时有效。如果内存池指针参数pool为空,则使用多段内存的第一个初始化为内存池,其他内存区域,作为空闲节点插入;如果内存池指针参数pool不为空,则把多段内存作为空闲节点,插入到指定的内存池。 |
说明:
- 由于动态内存管理需要管理控制块数据结构来管理内存,这些数据结构会额外消耗内存,故实际用户可使用内存总量小于配置项OS_SYS_MEM_SIZE的大小。
- 对齐分配内存接口LOS_MemAllocAlign/LOS_MemMallocAlign因为要进行地址对齐,可能会额外消耗部分内存,故存在一些遗失内存,当系统释放该对齐内存时,同时回收由于对齐导致的遗失内存。
- 非连续性内存区域接口LOS_MemRegionsAdd的LosMemRegion数组参数传入的非连续性内存区域需要按各个内存区域的内存开始地址升序,且内存区域不能重叠。
本节介绍使用动态内存的典型场景开发流程。
本实例执行以下步骤:
示例代码如下:
本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数ExampleDynMem。
#include "los_memory.h"
#define TEST_POOL_SIZE (2*1024)
#define MEMBOX_WR_TEST_NUM 828
__attribute__((aligned(4))) UINT8 g_testDynPool[TEST_POOL_SIZE];
VOID ExampleDynMem(VOID)
{
UINT32 *mem = NULL;
UINT32 ret;
/* 初始化内存池 */
ret = LOS_MemInit(g_testDynPool, TEST_POOL_SIZE);
if (LOS_OK == ret) {
printf("Mem init success!\n");
} else {
printf("Mem init failed!\n");
return;
}
/* 申请内存块 */
mem = (UINT32 *)LOS_MemAlloc(g_testDynPool, 4);
if (mem == NULL) {
printf("Mem alloc failed!\n");
return;
}
printf("Mem alloc success!\n");
/* 内存地址读写验证 */
*mem = MEMBOX_WR_TEST_NUM;
printf("*mem = %d\n", *mem);
/* 释放内存 */
ret = LOS_MemFree(g_testDynPool, mem);
if (LOS_OK == ret) {
printf("Mem free success!\n");
} else {
printf("Mem free failed!\n");
}
return;
}
输出结果如下:
Mem init success!
Mem alloc success!
*mem = 828
Mem free success!