FreeRTOS Heap Memory Management (1) - 内存分配介绍
原文链接:https://www.cnblogs.com/yanpio/p/14817341.html
1 介绍
FreeRTOS没有直接采用C标准库中提供的malloc() 和 free()函数进行内存分配,而是提供了5种内存分配方式,对应的源文件名分别为 heap1.c - heap5.c,位于FreeRTOS/Source/portable/MemMang/路径下。
不同的内存分配方式都提供了相同的接口函数,其中最重要的是如下两个接口函数:
/* 内存分配函数 */
void* pvPortMalloc( size_t *xWantedSize* );
/* 内存释放函数 */
void vPortFree( void * pv );
2 分配方式的特点
以下仅简单介绍各种分配方式的特点及适用范围,具体的源码分析会在后续的文章中逐个分析。
2.1 heap1
(1)特点:
heap1只提供了pvPortMalloc()接口,而没有vPortFree()(保留了接口,但是调用无效)。也就意味着此种内存分配方式不能释放内存,已分配的空间将一直存在于内存中。
heap1能够提供的最大内存由FreeRTOSConfig.h中的宏configTOTAL_HEAP_SIZE定义。内核预先分配一个尺寸为configTOTAL_HEAP_SIZE的数组(被称为FreeRTOS heap),每次调用pvPortMalloc()接口时,内核都会从数组中拿出一块,直到空闲数组被耗尽。此种分配方式可以避免内存碎片的问题。
(2)适用:
heap1适用于不需要删除任务的应用。一些重要的应用会禁止使用动态内存分配,因为动态分配会带来不确定性,内存碎片化,甚至会分配失败,这种应用可以考虑使用heap1。
(3)图示:
A为未分配时,内存的布局,B和C是创建一些Task后的内存布局。

2.2 heap2
FreeRTOS内核手册指出:建议使用heap4来替代heap2,因为heap4在heap2的基础上做了功能增强(主要是减少了内存碎片的风险)。
(1)特点:
heap2提供了pvPortMalloc()和vPortFree()接口,支持在应用中分配和释放内存。也是内核预先分配一个尺寸为configTOTAL_HEAP_SIZE的数组,并使用best fit algorithm(优先分配尺寸与所需尺寸最为接近的free block)来进行分配。
heap2可能导致内存碎片(memory fragmentation)问题。例如,现在有2个大小分别为 15, 25的free block,应用需要10,则根据best fit algorithm,将大小为15的block分为 10 和 5,则大小为5的碎片可能会浪费掉。
(2)适用:
heap2适用于重复分配和释放大小相同的block。因为每次分配和回收的尺寸都是相同的,也就避免了内存碎片的问题。
(3)图示:
下图说明了删除和添加任务时,内存的布局变化。

2.3 heap3
heap3简单封装了C标准库函数malloc() 和 free(),在调用库函数时挂起了其他任务,防止重入,使pvPortMalloc()和vPortFree()线程安全。而heap3能够提供的最大分配空间由连接器设置决定,不受configTOTAL_HEAP_SIZE宏值的影响。
此处顺便看一下heap3.c的源码,由于代码很简单,后面不再单独分析。
void * pvPortMalloc( size_t xWantedSize )
{
void * pvReturn;
vTaskSuspendAll() /* 分配时挂起所有任务,防止函数被重入 */
{
pvReturn = malloc( xWantedSize );
traceMALLOC( pvReturn, xWantedSize );/* 调试使用 */
}
( void ) xTaskResumeAll(); /* 分配后,恢复所有任务 */
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
/* 如果定义了hook函数,则在分配失败时,调用hook函数 */
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
void vPortFree( void * pv )
{
if( pv )
{
/* 与pvPortMalloc()类似,在free时挂起所有任务 */
vTaskSuspendAll();
{
free( pv );
traceFREE( pv, 0 );
}
( void ) xTaskResumeAll();
}
}
2.4 heap4
(1)特点:
heap4和heap2的功能基本一致,不同之处在于,heap4会将相邻的碎片合并起来。
此外,如果需要将FreeRTOS heap,即前面提到的内核预先分配的数组,分配在固定的内存上,则可以使用以下语法:
/* GCC编译环境, 以下声明可以将数组分配在.my_heap的内存上 */
uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__ ( ( section( ".my_heap" ) ) );
(2)适用:
heap4可以重复分配和释放大小不同的block(而heap2仅适合大小相同的block)。
(3)图示:
下图说明了heap4在分配时合并碎片的过程。

2.5 heap5
(1)特点:
heap5使用的分配策略和heap4完全一样,不同之处在于:heap4(heap1和heap2也是如此)只能在预先分配好的数组中(即供pvPortMalloc()分配的空间在内存中必须为连续空间)进行分配,而heap5可以在非连续的空间上进行分配。当然,需要在分配之前显式地调用vPortDefineHeapRegions()函数来进行内存初始化,主要是将各个分离的空间接合起来。
(2)适用:
适用于内存不连续的场景。
(3)图示:
heap5可以在下图所示的非连续空间上进行内存分配。具体的操作细节将在后续的源码分析中进行详细说明。

3 其他接口函数
除了上述介绍的pvPortMalloc()和vPortFree()函数,FreeRTOS还提供了如下几个相关的接口函数。
/* 获取空闲内存大小 */
size_t xPortGetFreeHeapSize( void );
/* 获取到目前最小剩余空间,可以用来了解任务实际使用内存的情况,以对内存分配的大小进行调节 */
size_t xPortGetMinimumEverFreeHeapSize( void );
/* 内存分配失败钩子函数,可以在内部分配失败时进行一些操作,如打印信息等 */
void vApplicationMallocFailedHook( void );
浙公网安备 33010602011771号