FreeRTOS Heap Memory Management (1) - 内存分配介绍

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后的内存布局。

image-20210527092655402

2.2 heap2

FreeRTOS内核手册指出:建议使用heap4来替代heap2,因为heap4heap2的基础上做了功能增强(主要是减少了内存碎片的风险)。

(1)特点:

heap2提供了pvPortMalloc()vPortFree()接口,支持在应用中分配和释放内存。也是内核预先分配一个尺寸为configTOTAL_HEAP_SIZE的数组,并使用best fit algorithm(优先分配尺寸与所需尺寸最为接近的free block)来进行分配。

heap2可能导致内存碎片(memory fragmentation)问题。例如,现在有2个大小分别为 15, 25free block,应用需要10,则根据best fit algorithm,将大小为15block分为 105,则大小为5的碎片可能会浪费掉。

(2)适用:

heap2适用于重复分配和释放大小相同block。因为每次分配和回收的尺寸都是相同的,也就避免了内存碎片的问题。

(3)图示:

​ 下图说明了删除和添加任务时,内存的布局变化。

image-20210527110627026

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)特点:

heap4heap2的功能基本一致,不同之处在于,heap4会将相邻的碎片合并起来。

​ 此外,如果需要将FreeRTOS heap,即前面提到的内核预先分配的数组,分配在固定的内存上,则可以使用以下语法:

/* GCC编译环境, 以下声明可以将数组分配在.my_heap的内存上 */
uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__ ( ( section( ".my_heap" ) ) );

(2)适用:

heap4可以重复分配和释放大小不同block(而heap2仅适合大小相同的block)。

(3)图示:

​ 下图说明了heap4在分配时合并碎片的过程。

image-20210527111458132

2.5 heap5

(1)特点:

heap5使用的分配策略和heap4完全一样,不同之处在于:heap4heap1heap2也是如此)只能在预先分配好的数组中(即供pvPortMalloc()分配的空间在内存中必须为连续空间)进行分配,而heap5可以在非连续的空间上进行分配。当然,需要在分配之前显式地调用vPortDefineHeapRegions()函数来进行内存初始化,主要是将各个分离的空间接合起来。

(2)适用:

​ 适用于内存不连续的场景。

(3)图示:

heap5可以在下图所示的非连续空间上进行内存分配。具体的操作细节将在后续的源码分析中进行详细说明。

image-20210527120120595

3 其他接口函数

​ 除了上述介绍的pvPortMalloc()vPortFree()函数,FreeRTOS还提供了如下几个相关的接口函数。

/* 获取空闲内存大小 */
size_t xPortGetFreeHeapSize( void );

/* 获取到目前最小剩余空间,可以用来了解任务实际使用内存的情况,以对内存分配的大小进行调节 */
size_t xPortGetMinimumEverFreeHeapSize( void );

/* 内存分配失败钩子函数,可以在内部分配失败时进行一些操作,如打印信息等 */
void vApplicationMallocFailedHook( void );
posted on 2021-05-27 12:08  Yanpeng0527  阅读(821)  评论(0编辑  收藏  举报