目录
说明:
一、FreeRTOS内存管理
1.1、动态分配与用户分配内存空间
1.2、标准C库动态分配内存缺点
1.3、FreeRTOS的五种内存管理算法优缺点
1.4、heap_1内存管理算法
1.5、heap_2内存管理算法
1.6、heap_3内存管理算法
1.7、heap_4内存管理算法
1.8、heap_5内存管理算法
二、FreeRTOS内存管理相关API函数
2.1、申请内存函数
2.2、释放内存函数
2.3、获取当前空闲内存的大小函数
说明:
关于内容:
1)以下内容多为概念了解与步骤分析
2)暂无个人示例代码,使用的是FreeRTOS的官方示例代码
3)若想移植代码测试的,请移步其它地方寻找,下文内容暂无个人示例代码供测试
关于其它:
1)操作系统:win 10
2)平台:keil 5 mdk
3)语言:c语言
4)板子:STM32系列移植FreeRTOS
一、FreeRTOS内存管理
1.1、动态分配与用户分配内存空间
在使用FreeRTOS创建任务、队列、信号量等对象的时,一般提供两种方法:
1)动态方式创建:FreeRTOS管理的内存堆中申请创建对象所需的内存,并且在对象删除后,可以将内存释放回收到FreeRTOS所管理的内存中;
2)静态方式创建,用户自己提供内存空间,并且使用静态方式占用的内存空间一般固定了,即使创建的任务队列被删除后,这些被占用的空间一般没有其他用途。
1.2、标准C库动态分配内存缺点
标准C库提供了函数malloc()和函数free()来动态地申请和释放内存。
那为什么不使用标准的C库自带内存管理算法呢?
1)占用大量的代码空间,不适合用在资源紧缺的嵌入式系统中;
2)没有线程安全的相关机制;
3)运行具有不确定性,每次调用这些函数时花费的时间可能都不相同;
4)内存碎片化-->内存空间会被分割成不同大小且不连续的区域。
关键是C库的内存管理算法不是为RTOS设计的,当然会出现各种问题啦,因此FreeRTOS提供了多种动态内存管理的算法,可针对不同的嵌入式系统。
1.3、FreeRTOS的五种内存管理算法优缺点
五种动态内存管理算法分别为:heap_1、heap_2、heap_3、heap_4、heap_5
名称,heap_1,优点:分配简单,时间确定,缺点:只允许申请内存,不允许释放内存
名称,heap_2,优点:允许申请和释放内存,缺点:不能合并相邻的空闲内存块会产生碎片、时间不定
名称,heap_3,优点:直接调用C库函数malloc()和free(),简单,缺点:速度慢、时间不定
名称,heap_4,优点:相邻空闲内存可合并,减少内存碎片的产生,缺点:时间不定
名称,heap_5,优点:能够管理多个非连续内存区域的heap_4,缺点:时间不定
1.4、heap_1内存管理算法
heap_1只实现了pvPortMalloc,没有实现pvPortFree;也就是说,该算法只能申请内存,无法释放内存。
适用场景:
创建的任务、队列、信号量等不需要删除。
实现原理:
管理的内存是一个大数组(10K),在申请内存时,heap_1通过计算大小,从数组中分出合适大小的内存,内存堆数字定义如下图1:
图1
1.5、heap_2内存管理算法
相当于heap_1内存管理算法,heap_2内存管理算法使用最适应算法,并且支持释放内存;
heap_2内存管理算法并不能将相邻的空闲内存块合并成一个大的空闲内存块;因此因此内存管理算法不可避免地会产生内存碎片。
什么是最适应算法?
假设heap有3块空闲内存(按内存块大小由小到大排序) : 5字节、25字节、50字节
现在新创建一个任务需要申请20字节的内存
第一步:找出最小的、能满足pvPortMalloc的内存: 25字节
第二步:把它划分为20字节、5字节;)返回这20字节的地址,剩下的5字节仍然是空闲状态,留给后续的pvPortMalloc使用
什么是内存碎片?
内存碎片是由于多次申请和释放内存,但释放的内存无法与相邻的空闲内存合并产生的。
内存碎片是怎么出现的?
如下图2:
图2
适用场景:
频繁的创建和删除任务,且所创建的任务堆栈都相同,此时不会出现碎片化的问题。
1.6、heap_3内存管理算法
直接调用C库函数malloc()和free(),这里不做了解。
1.7、heap_4内存管理算法
heap_ 4内存管理算法使用了首次适应算法,也支持内存的申请与释放,并且能够将空闲且相邻的内存进行合并,从而减少内存碎片的现象。
什么是首次适应算法?
首次适应算法:
假设heap有3块空闲内存(按内存块地址由低到高排序) : 5字节、50字节、25字节
现在新创建一个任务需要申请20字节的内存
第一步:找出第一个能满足pvPortMalloc的内存: 50字节
第二步:把它划分为20字节、30字节;返回这20字节的地址,剩下30字节仍然是空闲状态,留给后续的pvPortMalloc使用
如何合并相邻空闲内存块?
heap_4内存管理算法会把相邻的空闲内存合并为一个更大的空闲内存,这有助于减少内存的碎片问题。如下图3:
图3
适用场景:
频繁地分配、释放不同大小的内存。
1.8、heap_5内存管理算法
heap_5内存管理算法是在heap_4内存管理算法的基础上实现的,但是heap_5内存管理算法在heap_4内存管理算法的基础上实现了管理多个非连续内存区域的能力。
heap_ 5内存管理算法默认并没有定义内存堆,需要用户手动指定内存区域的信息,对其进行初始化。
问题:怎么指定一块内存?
使用如下结构体:
typedef struct HeapRegion
{
uint8_t* pucStartAddress; /*内存区域的起始地址*/
size_t xSizeInBytes; /* 内存区域的大小,单位:字节*/
} HeapRegion_t;
怎么指定多块且不连续的内存?
Const HeapRegion_t xHeapRegions[] =
{
{ (uint8_ _t *)0x80000000, 0x10000}, /*内存区域1 */
{ (uint8_ _t*)0x90000000, 0xA0000 }, /*内存区域2*/
{NULL, 0} /*数组终止标志*/
};
vPortDefineHeapRegions(xHeapRegions);
适用场景:
在嵌入式系统中,那些内存的地址并不连续的场景。
二、FreeRTOS内存管理相关API函数
2.1、申请内存函数
void * pvPortMalloc( size_t xWantedSize );
xWantedSize: 申请的内存大小,以字节为单位;假设申请内存为30,实际上内存减少不止30,因为会带上申请内存的结构体大小。
返回值:返回一个指针,指向已分配大小的内存。如果申请内存失败,则返回NULL。
2.2、释放内存函数
void vPortFree( void * pv );
pv:指针指向一个要释放内存的内存块(首地址放进来);申请多次内存,都在一个同缓存区间,则以最后一次申请的地址为准,进行释放。
2.3、获取当前空闲内存的大小函数
size_t xPortGetFreeHeapSize( void );
返回值:返回当前剩余的空闲内存大小