此笔记由个人整理
华为IOT LiteOS开发实战营
第三天
一、简介
- 内存管理模块管理系统的内存资源,它是操作系统的核心模块之一。主要包括内存的初始化、分配以及释放。
- 在系统运行过程中,内存管理模块通过对内存的申请/释放操作,来管理用户和OS对内存的使用,使内存的利用率和使用效率达到最优,同时最大限度地解决系统的内存碎片问题。
- HuaweiLiteOS的内存管理分为静态内存管理和动态内存管理,提供内存初始化、分配、释放等功能。
- 动态内存:在动态内存池中分配用户指定大小的内存块
- 优点:按需分配
- 缺点:内存池中可能出现碎片
- 静态内存:在静态内存池中分配用户初始化时预设大小(固定)的内存块
- 优点:分配和释放效率高,静态内存池中无碎片
- 缺点:只能申请到初始化预设大小的内存块,不能按需分配
- 动态内存:在动态内存池中分配用户指定大小的内存块
二、运作原理
动态内存
-
在内存资源充足的情况下,从系统配置的一块比较大的连续内存(内存池),根据用户需求,分配任意大小的内存块。当用户不需要该内存块时,又可以释放回系统供下一次使用。与静态内存相比,动态内存管理的好处是按需分配,缺点是内存池中容易出现碎片。
-
LiteOS动态内存支持DLINK和BESTLITTLE两种标准算法。
- DLINK
- 第一部分:堆内存的起始地址及堆区域总大小
- 第二部分:本身是一个数组,每个元素是一个双向链表,所有free节点的控制头都会被分类挂在这个数组的双向链表中**。每次申请内存的时候,会从这个数组检索最合适大小的free节点,进行分配内存**。每次释放内存时,会将该片内存作为free节点存储至这个数组,以便下次再利用。
- 第三部分:占内存池的大部分空间,是用于存放各节点的实际地址
-
BESTLITTLE
- 分配方式:每次分配时选择内存池中最小最适合的内存块进行分配。LiteOS动态内存管理在最佳适配算法的基础上加入了SLAB机制,用于分配固定大小的内存块,进而减小产生内存碎片的可能性。LiteOS内存管理中的SLAB机制支持可配置的SLABCLASS数目及每个CLASS的最大空间。
- 申请内存:先在满足申请大小的最佳SLABCLASS中申请,(比如用户申请20字节内存,就在SLAB块大小为32字节的SLABCLASS中申请),如果申请成功,就将SLAB内存块整块返回给用户,释放时整块回收。如果满足条件的SLABCLASS中已无可以分配的内存块,则继续向内存池按照最佳适配算法申请。需要注意的是,如果当前的SLABCLASS中无可用SLAB块了,则直接向内存池申请,而不会继续向有着更大SLAB块空间的SLABCLASS申请。
- 释放内存:先检查释放的内存块是否属于SLABCLASS,如果是SLABCLASS的内存块,则还回对应的SLABCLASS中,否则还回内存池中。
- DLINK
静态内存
- 静态内存实质上是一块静态数组,静态内存池内的块大小在初始化时设定,初始化后块大小不可变更。
- 静态内存池由一个控制块和若干相同大小的内存块构成。控制块位于内存池头部,用于内存块管理。内存块的申请和释放以块大小为粒度。
三、使用场景及开发流程
动态内存
- 使用场景
- 动态内存管理主要是在用户需要使用大小不等的内存块的场景中使用。
- 当用户需要分配内存时,可以通过操作系统的动态内存申请函数索取指定大小内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用。
- 开发流程(BESTLITTLE)
- 配置->初始化->申请动态内存->释放动态内存
- 配置:
- LOSCFG_MEMORY_BESTFIT:置为YES,选中内存管理算法中的BESTFIT算法。
- LOSCFG_KERNEL_MEM_SLAB:置为YES,打开内存管理中的SLAB机制。
- 初始化:
- 调用函数初始化用户指定的动态内存池,若用户使能了SLAB机制并且内存池中的可分配内存大于SLAB需要的最小内存,则会进一步初始化SLABCLASS。
- 申请动态内存:
- 调用函数从指定的内存池中申请指定大小的内存块,申请时内存管理先向SLABCLASS申请,申请失败后继续向堆内存空间申请,最后将申请结果返回给用户。在向堆内存空间申请时,会存在内存块的切分。
- 释放动态内存:
- 调用函数向指定的动态内存池释放指定的内存块,释放时会先判断该内存块是否属于SLABCLASS,若属于,则将该内存块还回SLABCLASS。否则,向堆内存空间释放内存块。在向堆内存空间释放时,会存在内存块的合并。
静态内存
- 使用场景
- 当用户需要使用固定长度的内存时,可以使用静态内存分配的方式获取内存,一旦使用完毕,通过静态内存释放函数归还所占用内存,使之可以重复使用。
- 开发流程
- 规划一片内存区域作为静态内存池。
- 系统内部将会初始化静态内存池。将入参指定的内存区域分割为N块(N值取决于静态内存总大小和块大小),将所有内存块挂到空闲链表,在内存起始处放置控制头。
- 系统内部将会从空闲链表中获取第一个空闲块,并返回该块的用户空间地址。
- 将该块内存加入空闲块链表。
- 系统内部清零静态内存块,将入参地址对应的内存块清零。
静态内存池区域,可以通过定义全局数组或调用动态内存分配接口方式获取。如果使用动态内存分配方式,在不需要静态内存池时,注意要释放该段内存,避免内存泄露。
四、操作系统抽象层
osal_malloc(size_t size)--按字节申请分配动态内存空间
osal_free(void *addr)--释放已经分配的动态内存空间
osal_zalloc(size_t size)--按字节申请分配动态内存空间,分配成功则初始化这块内存所有值为0
osal_realloc(void *ptr,size_t newsize)--重新申请分配动态内存空间
osal_calloc(size_t n,size_t size)--申请分配num个长度为size的动态内存空间
五、代码实践
- 创建工程,选择内存管理案例
基础实验
#include <osal.h>
static int mem_access_task_entry()//内存管理任务
{
uint32_t i = 0;
size_t mem_size;//内存大小
uint8_t* mem_ptr = NULL;//内存块指针
while (1)
{
mem_size = 1 << i++;//每次循环将申请到的内存扩大一倍
mem_ptr = osal_malloc(mem_size);//尝试申请分配内存
if(mem_ptr != NULL)
{
printf("access %d bytes memory success!\r\n", mem_size);//申请成功
osal_free(mem_ptr);//释放内存
mem_ptr = NULL;//将内存块指针置为NULL,避免野指针
printf("free memory success!\r\n");//释放成功
}
else
{
printf("BearPi: access %d bytes memory failed!\r\n", mem_size);
return 0;
}
}
}
int standard_app_demo_main()
{
osal_task_create("mem_access_task",mem_access_task_entry,NULL,0x400,NULL,11);
return 0;
}
- 结果
扩展实验
- 1、对内存指针赋值并打印。
- 2、重新分配指针的动态内存。
- 代码
*/
#include <osal.h>
static int mem_access_task_entry()//内存管理任务
{
uint32_t i = 0;
size_t mem_size;//内存大小1
size_t mem_size1;//内存大小2
uint16_t* mem_ptr = NULL;//内存块指针
while (1)
{
mem_size = 256;
mem_size1 = 1024;
mem_ptr = osal_malloc(mem_size);//尝试申请内存大小
printf("mem address1:%p\n",mem_ptr);
if(mem_ptr != NULL)
{
*mem_ptr = "hct 233";
printf("access %d bytes memory success!mem_ptr:%s\r\n", mem_size,*mem_ptr);
printf("mem address2:%p\n",mem_ptr);
osal_realloc(mem_ptr,mem_size1);//重新分配动态内存大小
if(mem_ptr != NULL)//判断是否赋值成功
{
printf("osal_realloc access %d bytes memory success!mem_ptr:%s\r\n", mem_size1,*mem_ptr);
}
else
{
printf("BearPi: access %d bytes memory failed!\r\n", mem_size1);
return 0;//失败退出
}
osal_free(mem_ptr);
mem_ptr = NULL;
printf("free memory success!\r\n");
return 0;
}
else
{
printf("BearPi: access %d bytes memory failed!\r\n", mem_size);
return 0;
}
}
}
int standard_app_demo_main()
{
osal_task_create("mem_access_task",mem_access_task_entry,NULL,0x400,NULL,11);
return 0;
}
- 结果