rt-thread内核的我们已经基本都学习过了,除了基本的线程操作和通信,内核部分还有内存管理和中断处理,本文主要就来说说内存管理相关问题。 目录
前言
一、为什么要内存管理
二、rt-thread 内存堆管理
2.1 rt-thread 内存分配
2.2 rt-thread内存堆管理方式
2.2.1 内存堆管理的3种方式
2.2.2 管理方式的程序配置
2.3 内存堆 api 函数
三、rt-thread 内存池
3.1 内存池的位置
3.2 内存池程序配置和控制块
3.3 内存池操作 api 函数
结语
前言 记得最初学习 rt-thread ,对于内存管理我也是简单看看然后一笔带过,当时觉得用不上,在我做的一些传感器单品项目上,对于内存管理确实知道与不知道没什么关系,但是随着认知的增长,项目复杂程度增加,发现内存管理还不可或缺,于是今时今日正好再次来更新 rt-thread记录,有必要好好的说一说。
rt-thread 有2种内存管理方式,分别是动态内存堆管理和静态内存池管理。
先不管这两种方式是怎么实现的,首先要明白一个问题,为什么要内存管理?
本 rt-thread 专栏记录的开发环境:
rt-thread记录(一、rt-thread 版本、rt-thread studio开发环境 及 配合cubemx开发快速上手)
rt-thread记录(二、rt-thread内核启动流程 — 启动文件和源码分析)
rt-thread 内核篇系列博文链接:
rt-thread记录(三、rt-thread 线程操作函数及线程管理与freertos的比较)
rt-thread记录(四、rt-thread 时钟节拍和软件定时器)
rt-thread记录(五、rt-thread 临界区保护)
rt-thread记录(六、ipc机制之信号量、互斥量和事件集)
rt-thread记录(七、ipc机制之邮箱、消息队列)
一、为什么要内存管理 什么是内存管理?为什么需要内存管理?
在我们的程序设计中,一些数据需要的内存大小需要在程序运行过程中根据实际情况确定,在用户需要一段内存空间时,向系统申请,系统选择一段合适的内存空间分配给用户,用户使用完毕后,再释放回系统,以便系统将该段内存空间回收再利用。
这样子不断的申请释放,如果没有内存管理,就会导致内存碎片,对程序产生极大的影响。
具体的原因我在下面博文有详细说明:浅谈 malloc 函数在单片机上的应用
如果看完上面的博文,就应该知道了内存管理的重要性。
我们确实直接在函数或者线程里面创建临时变量使用 线程栈 或 者系统栈处理临时变量,但是正如上面博文里面所说的,作为一个通用的操作系统,都会且必须得有自己的合理的内存管理方式。
这个在我们现在说明的 rt-thread 操作系统上是内核已经实现好的,我们得了解它。
知道了为什么,那么接下来我们就来了解一下 rt-thread 的这2种内存管理方式。
二、rt-thread 内存堆管理 现在看看内存堆管理, 所谓堆,看过我博文的朋友应该已经很熟悉了,对于裸机中的堆的位置,大小设定都应该会有一个详细的认知,如果还不知道堆,可以查看下面这篇博文一步到位的理解:
stm32的内存管理相关(内存架构,内存管理,map文件分析)
上文虽然是以stm32为例子说明,但是对于 c/c++ 程序编译后的存储数据段 在不同芯片上的顺序基本都是一样的,于是乎在我的又一篇博文中
嵌入式rtos的 任务栈 和 系统栈
就得到了一张内存分配的示意图:
2.1 rt-thread 内存分配 要理解 rt-thread 内存堆管理,首先得知道它管理的是哪一块内存,所以得知道 rt-thread 内存分配。
其实细心的朋友会发现,我在前面文章《rt-thread记录(二、rt-thread内核启动流程 — 启动文件和源码分析)》
中提到过 rt-thread 内存分配情况,在其中的 2.2.2 rt-thread 堆和栈空间说明(与freertos不同)有过说明:
通过上面的分析,我们可以得到 rt-thread 下的内存分配的示意图:
通过上面我们自己的分析,再结合官方说明文档的说明,我们应该能完全明白了什么是 rt-thread 的内存堆:
至此,我们可以确实的明白,rt-thread 内存管理管理的是哪一部分的内存了。
2.2 rt-thread内存堆管理方式 那么rt-thread 对于内存堆,是如何管理的呢?
说明:对于这部分,我个人记录是以知道为主,毕竟我们专栏以应用为主,我们了解会用即可,如果以后确实自己会需要自己写内存管理,肯定会单独更新一篇博文。
我们先前的基础介绍直接使用官网说明,本节后部分会说明下程序中是怎么选择使用的。
2.2.1 内存堆管理的3种方式 rt-thread 内存堆管理又根据具体内存设备划分为三种情况:
针对小内存块的分配管理(小内存管理算法) 针对大内存块的分配管理(slab 管理算法) 针对多内存堆的分配情况(memheap 管理算法) 这里套用官方的介绍做个简单说明(如果需要深入了解可以去官网查看):
2.2.2 管理方式的程序配置 我们上面 简单介绍了rt-thread 内存堆管理的3种方式,虽然我们没有详细分析内部是如何实现,那么我们也得了解在 rt-thread 工程中,是怎么配置使用哪一种管理方式,同时了解这些方式的实现在程序什么文件中。
在工程的rtconfig.h中有关于内存管理方式的配置:
上图是使用小内存堆管理算法。
如果是使用 slab 管理算法,需要宏定义如下:
#define rt_using_slab#define rt_using_heap 如果是使用 memheap 管理算法,需要宏定义如下:
#define rt_using_memheap_as_heap 具体的实现文件是在工程 mem.c 文件中,如下图:
rt-thread 内存管理详细的实现方式可以自行查看该文件,这里就不过多介绍。
2.3 内存堆 api 函数 对于 rt-thread 内存堆管理,是有自己的 malloc 函数,不能直接用 c 语言库中原始的malloc 函数。
其 三种管理算法提供的 api 都是相同的。
初始化:
首先是初始化函数:
/*小内存堆和slab 管理算法*/void rt_system_heap_init(void* begin_addr, void* end_addr);/*memheap 管理算法*/rt_err_t rt_memheap_init(struct rt_memheap *memheap, const char *name, void *start_addr, rt_uint32_t size)
我在《rt-thread记录(二、rt-thread内核启动流程 — 启动文件和源码分析)》板级硬件初始化 — rt_hw_board_init 小结中提到过 这个函数:
rt-thread内存堆初始化函数是在系统启动时候初始化的,而不是我们用户在main函数中再初始化的。
内存管理操作函数:
简单记录一下:
/*分配内存块参数:nbytes 需要分配的内存块的大小,单位为字节返回 ——分配的内存块地址 成功rt_null 失败*/void *rt_malloc(rt_size_t nbytes);/*释放内存块参数:ptr 待释放的内存块指针*/void rt_free (void *ptr);/*重分配内存块参数 描述rmem 指向已分配的内存块newsize 重新分配的内存大小返回 ——重新分配的内存块地址 成功*/void *rt_realloc(void *rmem, rt_size_t newsize);/*分配多内存块参数 描述count 内存块数量size 内存块容量返回 ——指向第一个内存块地址的指针 成功 ,并且所有分配的内存块都被初始化成零。rt_null 分配失败*/void *rt_calloc(rt_size_t count, rt_size_t size);/*设置内存钩子函数参数 描述hook 钩子函数指针*/void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size));/*上面函数的hook 函数接口参数 描述ptr 分配到的内存块指针size 分配到的内存块的大小*/void hook(void *ptr, rt_size_t size);/*释放内存钩子函数参数 描述hook 钩子函数指针*/void rt_free_sethook(void (*hook)(void *ptr));/*上面函数的hook 函数接口参数 描述ptr 待释放的内存块指针*/void hook(void *ptr);
释放内存块后要清空内存块指针,不然会成为野指针。
三、rt-thread 内存池 rt-thread 的第二种内存管理方式是 内存池,内存池是一种内存分配方式,用于分配大量大小相同的小内存块,它可以极大地加快内存分配与释放的速度,且能尽量避免内存碎片化。
他是为了提高内存分配的效率,并且避免内存碎片而产生的。
基本介绍套用官方说明:
内存池属于 内核对象!!支持线程挂起功能。
内核对线什么意思?就是我们前面说到的,线程,ipc机制,这些东西都是内核对象,所以存在 内存池 控制块。
有一个点要注意,内存池申请的内存块大小固定!
3.1 内存池的位置 内存池也是内存管理,那么他创建的之后在内存的什么位置呢?
首先明白内存池作为一个对象,其实可以认为是一个变量,那么他基本上就是属于.bss段的东西了,在rt-thread 中,他的位置应该是在 .bss段的。
我们按照上面的内存分配的示意图说明一下内存池申请的内存位置:
为了验证一下是否如此,可以初始化一个内存池,编译后查看一下.map文件:
内存池属于.bss段的数据,只不过他一般来说都是申请的相对比较大的一块内存空间,然后在这个大空间内自己有自己的分配管理方式。
3.2 内存池程序配置和控制块 内存池程序配置:
rt-thread 的内存池在程序中的配置和实现文件如下图:
内存池控制块:(因为是内对象,所以还是熟悉的配方,熟悉的味道~ ~)
struct rt_mempool{ struct rt_object parent; void *start_address; /* 内存池数据区域开始地址 */ rt_size_t size; /* 内存池数据区域大小 */ rt_size_t block_size; /* 内存块大小 */ rt_uint8_t *block_list; /* 内存块列表 */ /* 内存池数据区域中能够容纳的最大内存块数 */ rt_size_t block_total_count; /* 内存池中空闲的内存块数 */ rt_size_t block_free_count; /* 因为内存块不可用而挂起的线程列表 */ rt_list_t suspend_thread; /* 因为内存块不可用而挂起的线程数 */ rt_size_t suspend_thread_count;};typedef struct rt_mempool* rt_mp_t; 内存池具体管理方法的实现可以自己查看下源码,这里暂时不做深入研究。
3.3 内存池操作 api 函数 内存池作为内核对象,那么他的操作就和以前那些ipc机制,线程一样的方式,分为动态创建,静态初始化这些。
并不需要在 板级初始化的时候就初始化。用户可以自己选择用于不用。
简单记录说明一下内存池操作函数:
/*创建内存池参数 描述name 内存池名block_count 内存块数量block_size 内存块容量返回 ——内存池的句柄 创建内存池对象成功rt_null 创建失败*/rt_mp_t rt_mp_create(const char* name, rt_size_t block_count, rt_size_t block_size);/*删除内存池参数 描述mp rt_mp_create 返回的内存池对象句柄返回 ——rt_eok 删除成功*/rt_err_t rt_mp_delete(rt_mp_t mp);/*初始化内存池参数 描述mp 内存池对象name 内存池名start 内存池的起始位置size 内存池数据区域大小block_size 内存块容量返回 ——rt_eok 初始化成功- rt_error 失败*/rt_err_t rt_mp_init(rt_mp_t mp, const char* name, void *start, rt_size_t size, rt_size_t block_size);/*脱离内存池参数 描述mp 内存池对象返回 ——rt_eok 成功*/rt_err_t rt_mp_detach(rt_mp_t mp);/*分配内存块参数 描述mp 内存池对象time 超时时间返回 ——分配的内存块地址 成功rt_null 失败*/void *rt_mp_alloc (rt_mp_t mp, rt_int32_t time);/*释放内存块参数 描述block 内存块指针*/void rt_mp_free (void *block); 结语 本文从为什么要内存管理说起,了解了 rt-thread 的2种内存管理方式:内存堆 和 内存池。
相信不管你平时用不用动态内存申请,看了这篇文章以后也会对 rt-thread 的内存管理有一定的了解,在以后需要用到动态内存分配的同时也不会手足无措!
但是特别注意,不管使用哪种方式,在申请的内存使用完之后都必须及时释放,而且要注意清空相应的指针!
但是本文的重点在于理解 为什么要内存管理,和 内存管理管理的是哪一块的内存,对于如何实现内存管理我们只是借用了官方的说明,还是没有深入的研究分析。这个需要等以后我有机会自己写一套内存管理方式的时候,或许会单独开一篇文章深入分析。
谢谢!
基于极海半导体APM32F407系列MCU的伺服控制器应用方案
柔性触控技术迎来新突破,人机交互将全面实现
SpringBoot 2种方式快速实现分库分表,轻松拿捏!
有方科技出征印度Convergence跑出印度物联网市场加速度
电脑键盘的功能
RT-Thread记录(八、理解RT-Thread内存管理)
E-Bike电动自行车充电方案剖析
基于复合纸的具有自校正功能的金属钠阳极!
太阳能逆变器系统解决方案
TMC5160 步进电机驱动芯片介绍与使用
软硬件融合的概念和内涵
凤凰卫视与故宫博物院联合打造的高科技互动艺术展演《清明上河图3.0》在故宫箭亭广场正式开幕
pcb进行自动检测的作用
ARM3.5亿美元收购Apical 完善成像产品生态链
E703.15 单芯片超声波水表解决方案
阿尔泰科技搭建温室大棚的自动化解决方案
英伟达算力顶流Thor芯片发布 可取代驾驶舱芯片工作
行业 | 用于工业和汽车级碳化硅MOSFET功率模块的高温栅极驱动
上海人工智能核心企业突破1000家
耦合电容和分布电容的选用