一、堆和栈
堆(heap):就是一块空闲的内存
malloc:从堆里划出一块空间给程序使用
free:使用完,将这块内存空间标记为“空闲”,可以再次使用
栈(stack):函数调用的局部变量保存在栈中,当前程序的环境也保存在栈中
可以从堆中分配一块内存空间用作栈
二、内存管理方法
FreeRTOS内存管理接口函数为:pvPortMalloc、vPortFree,对应malloc、free
源码中默认提供5个文件,对应内存管理的五种方法
1.heap_1
只有pvPortMalloc,没有vPortFree,首先定义一个大数组,在pvPortMalloc时,从这个数组中分配空间。FreeRTOS在创建任务时,需要2个内核对象:task control block(TCB)和stack,使用heap_1时分配过程如下:
A:创建任务之前,整个数组为空闲状态
B:创建任务1,蓝色区域被划分
C:创建3个任务后数组的分配情况
2.heap_2
支持pvPortMalloc和vPortFree,使用最佳匹配算法(best fit)分配内存,碎片化严重
最佳匹配算法:假设heap有3块空闲内存:5字节、25字节、100字节,pvPortMalloc想申请20字节,会找出最小且能满足pvPortMalloc的内存:25字节,将其划分为20字节和5字节,其余5字节仍然是空闲状态可以使用。分配过程如下:
A:创建了3个任务
B:删除了一个任务,空闲内存包括顶部空间、被删除的TCB空间和被删除的Stack空间
C:创建了一个新任务,与被删除任务空间相同,刚好分配至原内存3
3.heap_3
heap_3使用标准C库中的malloc、free函数,所有堆大小由链接器的配置决定,配置项configTOTAL_HEAP_SIZE不起作用。
C库中的malloc、free函数并非线程安全的,heap_3会先暂停FreeRTOS的调度器,再去调用函数,实现线程安全
4.heap_4
heap_4也是使用大数组来分配内存,通过首次适应算法来分配内存,还会将相邻空闲内存合并为一个更大的空闲内存,有助于减少内存碎片的问题。
首次适应算法(first fit):假设有三块空闲内存:5字节、200字节、100字节,pvPortMalloc想申请20字节,首先找出第一个能满足pvPortMalloc的内存:200字节,将其划分为20字节和180字节,返回20字节的地址,其余180字节仍然为空闲状态。分配过程如下:
A:创建了3个任务
B:删除了一个任务,空闲Stack和TCB被合并,空闲内存包括顶层和任务两块内存
C:分配了一个Queue,从第一个空闲块中分配部分空间
D:分配了一个User数据,从Queue后面的空闲块中分配
E:释放Queue,User前后都包含一款空闲内存
F:释放User数据,中间的空闲内存合并为一个更大的空闲内存
5.heap_5
heap_5与heap_4内存分配、释放的算法一致,相比于heap_4,heap_5并不局限于管理一个大型数组,它可以管理多块、分隔开的内存。
内存地址不连续可以使用heap_5,在使用前需要确定这些内存块在哪、多大:
在使用pvPortMalloc 之前,必须先指定内存块的信息
使用vPortDefineHeapRegions 来指定这些信息
如何指定一块内存,使用如下结构体:
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 } // 表示数组结束
};
把xHeapRegions 数组传给vPortDefineHeapRegions 函数,即可初始化Heap_5。vPortDefineHeapRegions 函数原型如下:
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );
三、Heap相关函数
1.pvPortMalloc/vPortFree
作用:分配内存、释放内存
分配不成功返回值为NULL
void * pvPortMalloc( size_t xWantedSize );
void vPortFree( void * pv );
2.xPortGetFreeHeapSize
作用::当前还有多少空闲内存,Heap_3中无法使用
可以用来优化内存的使用情况,当所有内核对象都分配好,执行此函数发现返回2000,说明还有2000空闲内存,那么configTOTAL_HEAP_SIZE 就可减小2000。
size_t xPortGetFreeHeapSize( void );
3.xPortGetMinimumEverFreeHeapSize
作用:程序运行过程中,返回空闲内存容量的最小值,仅heap_4、heap_5支持
size_t xPortGetMinimumEverFreeHeapSize( void );
4.malloc 失败的钩子函数
在pvPortMallc函数内部,pvPortMallc失败时会调用此函数
使用时需要在FreeRTOSConfig.h 中,把configUSE_MALLOC_FAILED_HOOK 定义为1,并提供vApplicationMallocFailedHook 函数
void * pvPortMalloc( size_t xWantedSize )vPortDefineHeapRegions
{
......
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}