FreeRTOS入门——内存管理
FreeRTOS入门——内存管理
FreeRTOS是目前最受欢迎的实时操作系统(RTOS)内核之一,广泛应用于各种嵌入式系统中。内存管理是RTOS的核心功能之一,FreeRTOS提供了多种内存管理方案,以满足不同应用场景的需求。本文将详细介绍FreeRTOS的内存管理机制,帮助读者更好地理解和使用这一强大的功能。
一、堆和栈
堆(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;
}