学习FreeRTOS的内存管理首先要知道C语言的内存管理,对于C语言来说,它的库函数有malloc和free,但是这两个并不适用于FreeRTOS,原因如下:
1、malloc和free函数的实现较为复杂,其代码占用的空间量较大,对于嵌入式系统来说并不友好
2、不是线程安全的(什么是线程安全)
3、经常使用malloc和free会导致内存碎片化
4、malloc和free的运行时间不确定
5、有时候难以调试
堆和栈
堆:一块空闲内存,需要有管理函数
栈:从堆中分配一部分内存用于保存局部变量和现场环境
FreeRTOS的五种内存管理方式
heap_1:只分配不回收(简单、时间确定)
heap_2:分配+回收但是不合并会造成内存碎片(动态分配、最佳匹配)
heap_3:速度慢时间不定(使用标准库函数)
heap_4:分配+回收并且合并相邻的内存空间(解决碎片问题但是时间不定)
heap_5:在4的基础上还能合并不相邻的内存空间
heap_1
一些不允许使用动态内存的系统使用该方式,不会产生内存碎片
原理:首先定义一个堆的指定大小的数组,每次调用pvPortMalloc()函数都从数组中划分一部分空间,创建任务时要使用TCB和Stack这两个内核对象,就在这个大数组中分配
heap_2
现在已经不推荐使用heap_2了,能使用heap_4就使用heap_4,但是heap_2虽然也是在一个大数组中分配内存,但是它采取了最佳匹配算法,但是最佳匹配算法也会带来大量的内存碎片(除非每次最佳匹配都匹配到正好大小的内存空间),所以能用heap_4还是不要用这个了,因为heap_4能够合并相邻的内存碎片呀
heap_3
使用标准库函数malloc和free,所以自定义的堆的大小(configTOTAL_HEAP_SIZE)也不再起作用,而是由链接器决定
保证线程安全:在调用标准库函数时先暂停FreeRTOS的调度器
heap_4
和方式1和2相同也是使用大数组来分配内存,使用的是首次适应算法,同时会合并相邻空间的内存碎片。
首次适应算法:找到第一个足够大小的内存空间就直接分配(找到的第一个)
执行时间不确定,但是效率高于标准库函数
heap_5
算法和heap_4相同,但是heap_5可以管理多块分隔开的内存
内存是分隔开的那么使用前就要先进行初始化:确定这些内存块的位置和大小
typedef struct HeapRegion
{
uint8_t * pucStartAddress; // 起始地址
size_t xSizeInBytes; // 大小
} HeapRegion_t;
这是指定一块内存的结构体:一个地址加一个大小
下面是指定多块内存的结构体:起始地址+大小
HeapRegion_t xHeapRegions[] =
{
{ ( uint8_t * ) 0x80000000UL, 0x10000 }, // 起始地址0x80000000,大小0x10000
{ ( uint8_t * ) 0x90000000UL, 0xa0000 }, // 起始地址0x90000000,大小0xa0000
{ NULL, 0 } // 表示数组结束
};
初始化函数:
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );
把制定多块内存的结构体传入这个初始化函数即可
附录-内存分配的相关函数
分配释放内存:
void * pvPortMalloc( size_t xWantedSize );
void vPortFree( void * pv );
获取当前还有多大的空闲内存:(heap_3无法使用)
size_t xPortGetFreeHeapSize( void );
程序运行过程中空闲内存的最小值:(heap_4、5专用)
size_t xPortGetMinimumEverFreeHeapSize( void );
malloc失败的钩子函数:
void * pvPortMalloc( size_t xWantedSize )vPortDefineHeapRegions
{
......
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
注意使用前提:
configUSE_MALLOC_FAILED_HOOK == 1
还要存在钩子函数
vApplicationMallocFailedHook();