华为物联网操作系统LiteOS内核教程06-内存管理

1. liteos内核的内存管理
1.1. 内存管理
在系统运行的过程中,一些内存空间大小是不确定的,比如一些数据缓冲区,所以系统需要提供内存空间的管理能力,用户可以在使用的时候申请需要的内存空间,使用完毕释放该空间,以便再次利用。
huawei liteos 的内存管理模块通过对内存的申请/释放操作,来管理用户和os对内存的使用,使内存的利用率和使用效率达到最优,同时最大限度地解决系统的内存碎片问题。
1.2. 动态内存管理
动态内存管理,即在内存资源充足的情况下,从系统配置的一块比较大的连续内存(内存池),根据用户需求,分配任意大小的内存块。当用户不需要该内存块时,又可以释放回系统供下一次使用。
与静态内存相比,动态内存管理的好处是按需分配,缺点是内存池中容易出现碎片。
liteos动态内存支持 dlink 和 best little 两种标准算法。
1.2.1. dlink 动态内存管理算法
dlink动态内存管理结构如下图所示:
第一部分
堆内存(也称内存池)的起始地址及堆区域总大小。
第二部分
本身是一个数组,每个元素是一个双向链表,所有free节点的控制头都会被分类挂在这个数组的双向链表中。
第三部分
占用内存池极大部分的空间,是用于存放各节点的实际区域。
1.2.2. best little 算法(重点)
liteos 的动态内存分配支持最佳适配算法,即 best little,每次分配时选择内存池中最小最适合的内存块进行分配。
liteos 动态内存管理在最佳适配算法的基础上加入了 slab 机制,用于分配固定大小的内存块,进而减小产生内存碎片的可能性。
liteos 内存管理中的 slab 机制支持可配置的 slab class 数目及每个 class 的最大空间。
现以 slab class 数目为 4,每个 class 的最大空间为 512 字节为例说明 slab 机制:
在内存池中共有 4 个 slab class,每个 slab class 的总共可分配大小为 512 字节,第一个 slab class 被分为 32 个16 字节的 slab 块,第二个 slab class 被分为 16 个 3 2字节的 slab 块,第三个 slab class 被分为 8 个 64 字节的 slab 块,第四个 slab class 被分为 4 个 128 字节的 slab 块。这 4 个 slab class 是从内存池中按照最佳适配算法分配出来的。
初始化内存管理时,首先初始化内存池,然后在初始化后的内存池中按照最佳适配算法申请 4 个 slab class,再逐个按照 slab 内存管理机制初始化 4 个 slab class。
每次申请内存时,先在满足申请大小的最佳 slab class 中申请,(比如用户申请 20 字节内存,就在 slab 块大小为 32 字节的 slab class 中申请),如果申请成功,就将 slab 内存块整块返回给用户,释放时整块回收。如果满足条件的 slab class 中已无可以分配的内存块,则继续向内存池按照最佳适配算法申请。需要注意的是,如果当前的 slab class 中无可用 slab 块了,则直接向内存池申请,而不会继续向有着更大 slab 块空间的 slab class 申请。
释放内存时,先检查释放的内存块是否属于 slab class,如果是 slab class 的内存块,则还回对应的 slab class 中,否则还回内存池中。
1.2.3. 两种动态内存管理方法的选择
liteos动态内存管理的方法使用宏定义的方法使能,在用户工程目录下的os_config中的target_config.h文件中配置。
在该文件中,找到下面这两项宏定义,置为 yes 则表示使能:
开启best little 算法
#defineloscfg_memory_bestfityes
开启slab机制
#defineloscfg_kernel_mem_slabyes
1.3. 动态内存管理的应用场景
内存管理的主要工作是动态的划分并管理用户分配好的内存区间。
动态内存管理主要是在用户需要使用大小不等的内存块的场景中使用。当用户需要分配内存时,可以通过操作系统的动态内存申请函数索取指定大小内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使之可以重复使用。
2. 动态内存管理api
huawei liteos 系统中的内存管理模块管理系统的内存资源,主要提供内存的初始化、分配以及释放功能。
huawei liteos 系统中提供的内存管理 api 都是以 los 开头,但是这些 api 使用起来比较复杂,所以本文中我们使用 huawei iot link sdk 提供的统一api接口进行实验,这些接口底层已经使用 liteos 提供的api实现,对用户而言更为简洁,api列表如下:
osal的api接口声明在中,使用相关的接口需要包含该头文件,关于函数的详细参数请参考该头文件的声明。
相关的接口定义在osal.c中,基于liteos的接口实现在 liteos_imp.c文件中:
接口名 功能描述
osal_malloc 按字节申请分配动态内存空间
osal_free 释放已经分配的动态内存空间
osal_zalloc 按字节申请分配动态内存空间,分配成功则初始化这块内存所有值为0
osal_realloc 重新申请分配动态内存空间
osal_calloc 申请分配num个长度为size的动态内存空间
无论选择使用哪种动态内存管理算法,都使用该api。
2.1. osal_malloc
osal_malloc接口用于按字节申请分配动态内存空间,其接口原型如下:
void*osal_malloc(size_tsize){void*ret=null;if((null!=s_os_cb)&&(null!=s_os_cb->ops)&&(null!=s_os_cb->ops->malloc)) { ret=s_os_cb->ops->malloc(size); }returnret; }
该接口的参数说明如下表:
参数 描述
size 申请分配的内存大小,单位byte
返回值 分配成功 - 返回内存块指针
分配失败 - 返回null
2.2. osal_free
osal_free接口用于释放已经分配的动态内存空间,其接口原型如下:
voidosal_free(void*addr){if((null!=s_os_cb)&&(null!=s_os_cb->ops)&&(null!=s_os_cb->ops->free)) { s_os_cb->ops->free(addr); }return; }
内存块free之后,记得使内存块指针为null,否则会成为野指针!
该接口的参数说明如下表:
参数 描述
addr 动态分配内存空间的指针
返回值 无返回值
2.3. osal_zalloc
osal_zalloc接口用于按字节申请分配动态内存空间,分配成功则初始化这块内存所有值为0,其接口原型如下:
void*osal_zalloc(size_tsize){void*ret=null;if((null!=s_os_cb)&&(null!=s_os_cb->ops)&&(null!=s_os_cb->ops->malloc)) { ret=s_os_cb->ops->malloc(size);if(null!=ret) {memset(ret,0,size); } }returnret; }
该接口的参数说明如下表:
参数 描述
size 申请分配的内存大小,单位byte
返回值 分配成功 - 返回内存块指针
分配失败 - 返回null
2.4. osal_realloc
osal_realloc接口用于重新申请分配动态内存空间,其接口原型如下:
void*osal_realloc(void*ptr,size_tnewsize){void*ret=null;if((null!=s_os_cb)&&(null!=s_os_cb->ops)&&(null!=s_os_cb->ops->realloc)) { ret=s_os_cb->ops->realloc(ptr,newsize); }returnret; }
该接口的参数说明如下表:
参数 描述
ptr 已经分配了内存空间的指针
newsize 申请分配的新的内存大小,单位byte
返回值 分配成功 - 返回内存块指针
分配失败 - 返回null
2.5. osal_calloc
osal_calloc接口用于申请分配num个长度为size的动态内存空间,其接口原型如下:
void*osal_calloc(size_tn,size_tsize){void*p=osal_malloc(n*size);if(null!=p) {memset(p,0,n*size); }returnp; }
该接口的参数说明如下表:
参数 描述
n 申请分配内存块的数目
size 申请分配的每个内存块的内存大小,单位byte
返回值 分配成功 - 返回内存块指针
分配失败 - 返回null
3. 动手实验 —— 测试动态内存分配的最大字节
实验内容
本实验中将创建一个任务,从最小字节开始,不停的申请分配内存,释放分配的内存,直到申请失败,串口终端中观察可以申请到的最大字节。
实验代码
首先打开上一篇使用的 helloworld 工程,基于此工程进行实验。
在demo文件夹右击,新建文件夹osal_kernel_demo用于存放内核的实验文件(如果已有请忽略这一步)。
接下来在此文件夹中新建一个实验文件osal_mem_demo.c,开始编写代码:
/*使用osal接口需要包含该头文件*/#include/*任务入口函数*/staticintmem_access_task_entry(){uint32_ti=0;//循环变量 size_tmem_size;//申请的内存块大小 uint8_t*mem_ptr=null;//内存块指针 while(1) {/*每次循环将申请内存的大小扩大一倍*/ mem_size=1
正常情况下的服务演化之路
不间断电源的三个分类,切换到电池供电
第二十届电感变压器产业链峰会正式启动!
宋慧乔和宋仲基大婚,玩“太阳的后裔”番外篇,科技界的小米和诺基亚专利授权也“大婚”了
医用氧传感器的介绍,为什么RGM需要氧传感器
华为物联网操作系统LiteOS内核教程06-内存管理
FPGA图像处理的Sobel边缘检测
PCB设计过程中进行回流路径分析:高速信号回流路径
中性点接地电阻柜一般是多大尺寸的
指纹识别或成过去式,苹果备将Face ID应用到所有硬件数码产品中
电梯安装无线网桥的方法解析
德州仪器(TI)推出超节能OMAP4470应用处理器
老邢点评:新一轮投资潮悄然而至,VR市场有望复苏
总结常见的电脑蓝屏的原因和解决措施
墨奇M1智能通行一体机为用户提供方便、安全身份认证体验
数据中心各个阶段元宇宙是什么样
PoE解决方案的关键因素及设计需要考虑哪些问题
华强北破解AirPods了吗?华强北AirPods破解iOS16盗版检测
科裕智能科技感应锁918-3-D介绍
捷易科技2023CPSE安博会圆满落幕!