本章将介绍LiteOS内核的内存管理模块
1. 基本概念与简介
内存管理也是操作系统重要的组成部分,在程序运行过程中,常常需要为不同任务提供内存资源,涉及到内存的初始化,分配,释放等操作,因此为了高效地进行这些操作,我们也需要一套内存管理系统。LiteOS内存管理系统主要提供动态内存管理和静态内存管理两种管理方式。
对于动态内存管理,其主要运作机制是当需要动态分配内存时,系统从动态内存池中选取一块较大的连续内存,按照要求用户要求,从中选取任意大小的内存块分配给等待资源的任务,当该任务不再需要内存块时,释放该内存块。动态内存管理在内存分配上具有较强的灵活性,但是比较容易出现内存池碎片(即不连续内存)。
对于静态内存管理,其主要运作机制是当需要静态分配内存时,系统从静态内存池中分配一块初始化时确定大小的内存块给目标任务,在任务执行结束后释放内存块,由于大小固定,不会出现碎片化内存池的现象,但是也缺少了一定的灵活度。
下面将分别对LiteOS操作系统下动态和静态内存的管理方式进行详细分析
动态内存
目前主要有bestfit和bestfit_little两种管理办法
Bestfit内存管理算法又称最佳适配算法,其主要思想是从全部的空闲区域中找出大小最小,存储空间利用率最高的分配方式。为此,需要把空闲内存块从大到小依次排列,从表头查找一个最为合适的空闲区进行分配。其结构主要由三个部分组成,如图所示
第一部分包括了起始地址,动态内存池总大小两个数据
第二部分是一个以双向链表为元素的数组,每个双向链表都存储了一定大小范围内的free内存节点,其大小从小到大排列依次是<size< (是指系统允许的最小节点单位,单位是bytes), <size< ,以此类推,第n个双向链表存储的是所有size为<size< 的free节点,当系统需要分配动态内存的时候,按照要求的大小,匹配数组中最合适的free节点,当分配的节点使用结束后,该节点重新回到数组等待下一次分配。
第三部分是用于存放各个节点的实际区域,占用较大的内存池空间。其每个节点都是一个LosMenDynNode结构体,其主要内容与结构图如下
typedef struct {
union {
LOS_DL_LIST freeNodeInfo; /* Free memory node */
struct {
UINT32 magic;
UINT32 taskId : 16;
};
};
struct tagLosMemDynNode *preNode;
UINT32 sizeAndFlag;
} LosMemCtlNode;
typedef struct tagLosMemDynNode {
LosMemCtlNode selfNode;
} LosMemDynNode;
其中,结构体的第一个union成员里面含有释放和分配两种不同的属性,当该节点是属于被释放的节点时,将被加载到第二部分的数组链表中,如果作为分配使用的节点,会自动保存magic变量和申请任务的ID;结构体第二个成员变量preNode保存上一个节点的指针,便于串联所有节点;结构体第三个成员变量sizeAndFlag,记录了第四个部分data的大小和节点的状态(used/free);第四个部分data是记录节点的具体存储内容的。同时,当申请到的节点包含的数据空间首地址不符合对齐要求时需要进行对齐,通过增加Gap域确保返回的指针符合对齐要求。
Bestfit_little算法是Bestfit的升级版,其主要变动在于引入了slab机制,slab机制可用于分配固定大小的内存块,弥补Bestfit产生的碎片化问题。具体实现上,LiteOS的内存管理提供了用于配置slab class的数目和class大小的API。其构成如图所示,第一部分是负责管理slab class的内存池头部,第二部分是大小相同的slab class,但是每个slab class由于采用bestfits分配机制,其内部的slab块大小不同,例如对于初始化大小为512bytes的四个class slab,第一个被分配2^4=16bytes大小的内存块,共32个,第二个则为2^5=32bytes,共16个,以此次类推,当用户要求配置20bytes的内存空间时,即可使用第二个slab class中的内存块,将其整块分配给目标任务,在释放时也整块收回,按照其属性选择回到slab class或者是第三部分,避免了出现细小的内存碎片;第三部分是剩余的内存池空间,当从第二部分申请slab class失败的时候,就按照bestfits算法冲这个部分申请对应大小的内存空间,成为一个LosHeapNode结构,多个该结构以链表形式连接,同时将链表嵌入第一部分的头尾节点中·。
静态内存
静态内存管理相对比较简单,在LiteOS中的管理结构图如下所示
其结构主要由一个内存控制块和若干个大小相同的内存块组成,控制块位于头部,内存块的大小在初始化时决定,不可变更,内存的分配和释放都以块的大小为单位,例如块大小为32bytes,在申请内存大小为32bytes以下时都分配一个内存块,32<size<64时都是分配两个块。
2. 动态内存实例分析
2.1 前提条件
前提条件:1.在kernel/include/los_config.h文件中配置动态内存池起始地址大小,即OS_SYS_MEM_ADDR,OS_SYS_MEM_SIZE两个配置项,一般取默认值即可。2.在系统的初始化make menuconfig菜单手动配置内存管理模块,选择使能bestfit动态内存管理方法,关闭bestfit_little相关的功能,使能内存任务统计。
2.2 具体步骤
在完成了上述配置过后,就可以在LiteOS Stdio上创建新工程了,其主程序部分代码如下所示
#define TEST_POOL_SIZE (2*1024*1024)//分配4kb大小的内存池
UINT8 g_testPool[TEST_POOL_SIZE];//定义数组
VOID Example_DynMem(VOID)//主函数入口
{
UINT32 *mem = NULL;
UINT32 ret;
ret = LOS_MemInit(g_testPool, TEST_POOL_SIZE);//初始化,生成大小为4kb内存池,若初始化成功则返回LOS_OK
if (LOS_OK == ret) {
dprintf("内存池初始化成功!\n");
} else {
dprintf("内存池初始化失败!\n");
return;
}
/*分配内存*/
mem = (UINT32 *)LOS_MemAlloc(g_testPool, 4);//在内存池中分配一个大小为4Bytes的内存块
if (NULL == mem) {
dprintf("内存分配失败!\n");
return;
}
dprintf("内存分配成功\n");
/*赋值*/
*mem = 828;//向内存块中写入内容
dprintf("*mem = %d\n", *mem);
/*释放内存*/
ret = LOS_MemFree(g_testPool, mem);//释放内存块。
if (LOS_OK == ret) {
dprintf("内存释放成功!\n");
} else {
dprintf("内存释放失败!\n");
}
return;
}
运行结果为
内存池初始化成功!
内存分配成功
*mem = 828
内存释放成功!
在这个实例中,主要简单地演示了动态内存从初始化-分配内存空间块-往块中写入数据打印-释放内存的过程。
3. 静态内存实例分析
3.1 前提条件
在操作系统初始化界面的menuconfig菜单下配置静态内存管理模块,具体路径为Kernel ---> Memory Management。使能LOSCFG_KERNEL_MEMBOX和LOSCFG_KERNEL_MEMBOX_STATIC两项,分别表示使能membox内存管理和选择静态方式实现membox;同时关闭LOSCFG_KERNEL_MEMBOX_DYNAMIC项的使能,这一项表示动态内存。同时在在内存池中规划出一片区域用于初始化。
3.2 具体步骤
在完成上述准备工作后,就可以在LiteOS Stdio下新建工程开始写程序了,主程序部分代码如下所示
VOID Example_StaticMem(VOID)
{
UINT32 *mem = NULL;
UINT32 blkSize = 10;//内存块大小
UINT32 boxSize = 100;//内存块数量
UINT32 boxMem[1000];//静态内存池大小
UINT32 ret;
ret = LOS_MemboxInit(&boxMem[0], boxSize, blkSize);//静态内存初始化函数
if(ret != LOS_OK) {
printf("内存池初始化失败!\n");
return;
} else {
printf("内存池初始化成功!\n");
}
/*申请内存块*/
mem = (UINT32 *)LOS_MemboxAlloc(boxMem);//为mem指针申请内存块
if (NULL == mem) {
printf("内存分配失败!\n");
return;
}
printf("内存分配成功\n");
/*赋值*/
*mem = 828;//向申请好的内存块中赋值
printf("*mem = %d\n", *mem);
/*清除内存内容*/
LOS_MemboxClr(boxMem, mem);
printf("清除内存内容成功\n *mem = %d\n", *mem);
/*释放内存*/
ret = LOS_MemboxFree(boxMem, mem);
if (LOS_OK == ret) {
printf("内存释放成功!\n");
} else {
printf("内存释放失败!\n");
}
return;
}
其输出结果如下所示
内存池初始化成功!
内存分配成功
*mem = 828
清除内存内容成功
*mem = 0
内存释放成功!
在这个实例中,主要简单地演示了静态内存从初始化内存池-分配内存空间块给指定任务-往块中写入数据打印-清除内存块中的数据-释放内存块的过程。
注:程序中的相关接口函数可以在gitee上的LiteOS仓库找到。