10.1.2. FreeRTOS内存管理

在C语言的库函数中,有malloc, free等函数,但在FreeRTOS中,他们不适用

  • 不适用在资源紧缺的嵌入式系统中

  • 这些函数的实现过于复杂,占据的代码空间大

  • 并非线程安全

  • 运行有不确定性,每次调用这些函数时花费的时间可能都不相同

  • 内存碎片化

  • 使用不同的编译器时,需要进行复杂的配置

  • 有时难以调试

10.1.2.1. FreeRTOS内存管理方法

FreeRTOS中内存管理的接口函数为: pvPortMalloc , vPortFree ,对应C库的malloc, free

文件

优点

缺点

heap_1.c

分配简单,时间确定

只分配,不回收

heap_2.c

动态分配,最佳匹配

碎片,时间不确定

heap_3.c

调用标准库函数

速度慢,时间不定

heap_4.c

相邻空闲内存可合并

可解决碎片问题,时间不定

heap_5.c

在heap_4基础上支持分割的内存块

可解决碎片问题,时间不定

10.1.2.2. Heap_1

它只实现了pvPort, 没有实现vPortFree

如果你的程序不需要删除内核对象,那么可以使用heap_1

  • 实现最简单

  • 没有碎片问题

  • 一些要求非常严格的系统里,不允许使用动态内存,就可以使用heap_1

它的实现原理很简单,首先定义一个大数组

/* A few bytes might be lost to byte aligning the heap start address. */
#define configADJUSTED_HEAP_SIZE    ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )

/* Allocate the memory for the heap. */
#if ( configAPPLICATION_ALLOCATED_HEAP == 1 )

/* The application writer has already defined the array used for the RTOS
* heap - probably so it can be placed in a special segment or address. */
    extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
    static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */

然后,对于pvPortMalloc调用时,就从这个数组中分配空间

FreeRTOS在创建任务时,需要2个内核对象: task control block(TCB), stack

使用heap_1时,内存分配过程如下图所示

  • A: 创建任务之前整个数组都是空闲的

  • B: 创建第一个任务之后,蓝色区域被分配出去了

  • C: 创建第三个任务之后的数组使用情况

../../_images/heap_1.png

10.1.2.3. Heap_2

Heap_2之所以还保留,只是为了兼容以前的代码.新设计中不再推荐使用Heap_2,建议使用Heap_4来代替Heap_2,更加高效

10.1.2.4. Heap_3

Heap_3使用标准C库里的malloc, free函数,所以堆大小由链接器的配置决定

10.1.2.5. Heap_4

跟Heap_1和Heap_2一样,Heap_4也是使用大数组来分配内存

Heap_4使用 首次适应算法(first fit) 来分配内存.它还会把相邻的空闲内存合并为一个更大的空闲内存,这有助于较少内存 的碎片问题

首次适应算法:

  • 假设堆中有3块空闲内存:5字节,200字节,100字节

  • pvPortMalloc想申请20字节

  • 找出第一个能满足pvPortMalloc的内存: 200字节

  • 把它划分为20字节,180字节

    • 返回这20字节的地址

    • 剩下的180字节仍然是空闲状态,留给后续的pvPortMalloc使用

注解

Heap_4执行的时间是不确定的,但它的效率高于标准库的malloc, free

10.1.2.6. Heap_5

Heap_5分配内存,释放内存的算法是跟Heap_4一样的.不同的是,Heap_5并不局限于管理一个大数组:它可以管理多块,分割开的内存.

既然内存是分割开的,那么就需要进行初始化,确定这些内存块在哪,有多大

typedef struct HeapRegion
{
    uint8_t *pucStartAddress;
    size_t  xSizeInBytes;
} HeapRegion_t;

HeapRegion_t xHeapRegions[] = {
    {(uint8_t *)0x80000000UL, 0x10000},
    {(uint8_t *)0x90000000UL, 0xa0000},
    {NULL, 0}
};

void vPortDefineHeapRegions(const HeapRegion_t * const pxHeapRegions);

10.1.2.7. Heap相关的函数

void *pvPortMalloc(size_t xWantedSize);
void vPortFree(void *pv);
size_t xPortGetFreeHeapSize(void);
//返回程序运行过程中,空闲内存容量的最小值,只有Heap_4, Heap_5支持此函数
size_t xPortGetMinmumEverFreeHeapSize(void);
  • malloc失败的钩子函数

void *pvPortMalloc(size_t xWantedSize)
{
    ...
    #if (configUSE_MALLOC_FAILED_HOOK == 1)
        if(pvReturn == NULL)
        {
            extern void vApplicationMallocFailedHook(void);
            vApplicationMallocFailedHook();
        }
    #endif
}

注解

使用此钩子函数需要在FreeRTOSConfig.h中,把configUSE_MALLOC_FAILED_HOOK定义为1