目录
前言:本课程参考韦东山老师视频,连接放在最后。
#RTOS内存管理介绍
后续的RTOS学习中,通常会使用这些对象 任务(task) 队列(queue) 多线程(semaphores) 事件( event group),这些对象定义地时候在内存中通常是,动态分配内存(使用分配,不用释放),也就是通过堆,来申请内存或者释放内存,这样做有一个好处,简化了程序设计。
动态分配内存是C语言中的知识,但是与RTOS联系紧密,相关函数有 mallc(申请内存) free(释放内存),但是这些函数在RTOS系统里面并不适用,原因是,嵌入式系统资源紧缺,函数定义实现过于复杂,代码占用空间太大。
因为上述的缺点,在RTOS中并不使用mallc free对堆进行分配和释放,而是通过 pvPortMalloc vPortFree 这两个函数进行内存的分配与释放,pvPortMalloc对堆的内存进行分配 vPortFree 进行释放堆的内存。
pvPortMalloc对应着C语言中的mallc vPortFree对应着C语言中的free函数,也就是平替。
#堆定义
heap 堆
解释:空闲的内存,通过函数占用或者释放
mallc :从堆里面申请一个内存给程序进行使用
free :将已经分配给程序的堆,进行释放,释放过后的堆可用于在此使用。
#栈定义
stack 栈
解释:调用函数的局部变量LR寄存器,还有切换函数保存的环境也是在栈中
关联:从堆里面分配一块内存空间可以当做栈使用
#RTOS四种堆分配方案
RTOS里面对应堆的管理有5种文件,每种文件分别对应着不同的内存管理方法,有着不同的效果。
#Heap_1.c
相关介绍:heap_1.c 这个文件只实现了,pvPortMalloc 没有实现 vPortFree 也就是只实现了分配堆内存,没有实现释放堆内存。
如果程序不需要删除,堆内存对象可以使用这个文件,对堆内存进行管理,如果在一些严格的系统里面,如果不允许使用动态内存(就是不允许分配内存,使用过后回收内存),就可以使用这个函数,进行分配内存,因为这个文件不回收内存。
A在创建任务之前整个Heap都是空着的,B是分配一个任务之后heap的使用情况,C是3个任务之后heap的使用情况,这个时候如果有任务结束,这些空间是不会被收回的。
#Heap_2.c
相关介绍:heap_2.c使用最佳匹配算法(best fit)进行分配内存空间,支持释放内存空间也就是vPortFree heap_2.c被保留是为了兼容以前的代码,新设计中建议使用heap_4来代替heap_2来使用。
最佳匹配算法:当任务需要30个字节的heap,算法会寻找,容量相同的内存空间如果没有,找到差值最小的内存空间进行分配,而二者的差值,会被重新使用分配。
heap_2.c:释放内存空间:在任务结束之后,所占用的内存空间会被释放,因为heap_2.c支持
vPortFree 也就能释放内存空间。
A阶段创建了3个任务,B阶段释放了一个任务的内存空间,C阶段将释放掉的内存空间重新进行分配,但是空闲空间大小不会被合并。也有事当堆释放时,会有严重的碎片问题。
#Heap_3.c
相关介绍:heap_3.c使用标准库里面的 mallc free 函数 这里堆的大小由连接器配置,配置选项 configTOTAL_HEAP_SIZE(stm32cubleMX选项) 不在起到作用,heap_3.c 支持 释放 分配堆内存。
C库里面的 malloc free 函数并非线程安全的,heap_3中首先暂停RTOS的任务调度器,再去调用这些函数,使用这些方法实现了线程安全。
#Heap_4.c
相关介绍:heap_4.c 和heap_1.c heap_2.c 堆管理文件一样,都是靠着大数组来分配内存,heap_4.c使用首次适应算法(first fit)来分配内存,同时会把相邻空间分配为更大的空间,有效减少内存碎片的问题。
首次适应算法:如果pvPortMalloc需要申请A大小的内存空间,算法会寻找现有内存空间,从中划分出A大小的空间进行分配,剩下的仍然给 pvPortMalloc 进行分配。
heap_4.c想对于heap_2.c,当对于堆释放内存的时候,前者可以合并已经释放的内存空间,合并为更大的内存空间。
#Heap_5.c
相关介绍:支持 pvPortMalloc vPortFree 分配内存释放内存,可以管理多块分隔开的内存,如果内存的地址不连续,可以使用 heap_5.c 如果内存是分开的不连续的,在使用 pvPortMalloc 进行分配之前就需要初始化,确定内存块在哪里,多大。
内存初始化函数:vPortfineHeapRegions 来指定信息
typedef struct HeapRegion
{
uint8_t * pucStartAddress; // 起始地址
size_t xSizeInBytes; // 大小
} HeapRegion_t;
结构体类型定义:这个结构体,有两个成员变量,分别是起始地址,还有地址大小,如果要定义多块内存,就需要使用 HeapRegion_t 去声明一个数组进行使用
HeapRegion_t xHeapRegions[] =
{
{ ( uint8_t * ) 0x80000000UL, 0x10000 }, // 起始地址0x80000000,大小0x10000
{ ( uint8_t * ) 0x90000000UL, 0xa0000 }, // 起始地址0x90000000,大小0xa0000
{ NULL, 0 } // 表示数组结束
这里定义数组,表示多块内存,定义格式需要注意。
#stm32cublemx对堆的配置
这里下图是堆的配置,heap的大小在3072个字节,使用heap_4进行分配。
#配置堆相关函数
#申请内存函数
size_t xPortGetMinimumEvenFreeHeapSize(void);
这个函数返回值,是运行程序所剩内存空间的最小值,只有heap_4.c heap_4.c支持这个函数。如果调用函数返回值很小,也就是堆内存分配出去很多,可以适当的将,内存空间调大。
#钩子函数
void *pvPortMalloc(size_t xWantedSize)vPortDefineHeapRegions
{
#if(configUSE_MALLOC_FAILED_HOOK == 1)
{
if(pvReturn == NULL)
{
extern void vAPPlicationMallocFailedHook(void);
vAPPlicationMallocFailedHook();
}
}
}
pvPortMalloc这个函数如果申请内存失败,在内部可以定义申请一个钩子函数,返回一些信息,这个失败调用的函数可以自己定义使用。
欢迎指正,希望对你有所帮助!!!