【FreeRTOS】内存管理
内存管理
- FreeRTOS内存管理
- 内存碎片
- heap_1内存分配方法
- heap_2内存分配方法
- heap_3内存分配方法
- heap_4内存分配方法(常用)
- heap_5内存分配方法
- 总结
- 内存管理实验
FreeRTOS内存管理
FreeRTOS创建任务、队列、信号量等数据需要内存时,有两种内存创建方法:一时动态申请所需的RAM,二是静态申请所需RAM,一般使用静态创建内存的函数名结尾带有"Static"。
在标准C库中提供了malloc()和free()函数来实现动态内存管理,但由于部分原因限制使用:
- 在小型的嵌入式系统中效率不高
- 会占用很多的代码空间
- 不是线程安全
- 会导致内存碎片
- 使链接器的配置变得复杂
因此FreeRTOS的内存管理方法中提供了pvPortMalloc()来替代malloc()申请内存,vPortFree()函数来替代free()释放内存。
FreeRTOS提供了5种内存分配方法,使用FreeRTOS时任选其一即可,分别是:heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c,这5个文件再FreeRTOS源码中,路径:FreeRTOS->Source->portable->MemMang中。
动态内存分配需要一个内存堆,在FreeRTOS中内存堆为ucHeap[],大小为configTOTAL_HEAP_SIZE。不管是哪种内存分配方法,它们的内存堆都为ucHeap[],而且大小都为configTOTAL_HEAP_SIZE,内存堆在文件heap_x.c(x为1-5)中定义,比如heap_1.c文件中有如下定义:
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //需要用户自行定义内存堆
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; //编译器决定
#endif
当宏configAPPLICATION_ALLOCATED_HEAP为1时由用户自行定义内存堆,否则有编译器决定,默认都是编译器决定。
内存碎片
内存堆在经过多次申请和释放后,会产生很多内存很小的内存块,这些内存卡地址不连续,无法重新申请使用,这种内存块就叫内存碎片。
heap_1内存分配方法
特性:
- 适用于一旦创建好任务、信号量和队列就不在删除的应用
- 具有可确定性(执行所华花费的时间大多数都是一样的),而且不会导致内存碎片(因为heap_1算法无法处理内存碎片)
- 代码实现和内存分配过程非常简单,内存是从一个静态数组中分配到的,也就是适合于不需要动态分配内存的应用
heap_1算法没有提供内存释放函数
heap_2内存分配方法
其实与heap_1算法差不多一致,唯一的区别就是多了内存释放函数,通过引入内存块的概念和一个链表结构,来实现内存释放,该链表结构包含当前内存块的大小和指向链表中下一个空闲内存块。
特性:
- 可以使用在需要重复申请释放内存的应用,但是会产生内存碎片
- 具有不可确定性,但远比标准C中的malloc()和free()效率高
heap_3内存分配方法
特性:
- 需要编译器提供一个内存堆,编译器库要提供malloc()和free()函数,比如使用STM32的话可以通过修改启动文件中的Heap_Size来修改内存堆的大小
- 具有不确定性
- 可能会增加代码量(因为内部其实是调用malloc和free)
heap_3其实就是对标准C库中的malloc和free进行了简单的封装,所以才需要编译器库提供两个函数,也才会导致代码量增加
heap_4内存分配方法(常用)
heap_4其实是在heap_2的基础上进一步优化,依然使用和heap_2一样的链表结构,并额外定义两个局部静态变量xStart和pxEnd来表示链表头和尾
特性:
- 可以用在需要重复申请释放内存的应用中
- 不会产生严重的内存碎片,即使分配的内存大小是随机的
- 具有不确定性,但远比标准C中的malloc()和free()效率高
heap_5内存分配方法
heap_5使用了和heap_4相同的合并算法,内存管理实现起来基本相同,但是heap_5运行内存堆跨越多个不连续的内存段,比如 STM32 的内部 RAM 可以作为内存堆,但是 STM32 内
部 RAM 比较小,遇到那些需要大容量 RAM 的应用就不行了,如音视频处理。不过 STM32 可
以外接 SRAM 甚至大容量的 SDRAM,如果使用 heap_4 的话你就只能在内部 RAM 和外部
SRAM 或 SDRAM 之间二选一了,使用 heap_5 的话就不存在这个问题,两个都可以一起作为
内存堆来用。
如果使用 heap_5 的话,在调用 API 函数之前需要先调用函数 vPortDefineHeapRegions ()来
对内存堆做初始化处理,在 vPortDefineHeapRegions()未执行完之前禁止调用任何可能会调用
pvPortMalloc()的 API 函数!
总结
heap_1最简单,但是只有内存申请函数,没有内存释放函数
heap_2在heap_1的基础上多了内存释放函数
heap_3只是对标准C库中的malloc和free进行封装,并提供了线程保护
heap_4在heap_2的基础上对内存碎片进行了处理
heap_5在heap_4的基础上实现对不连续内存堆的应用
内存管理实验
在做内存管理实验时,通过pvPortMalloc(30)申请了30个字节的内存空间,在申请前通过xPortGetFreeHeapSize()获取的剩余内存为7136,申请后发现变成了7096,说明申请了40个字节的内存,但实际上只申请了30,这是因为除开所申请的30字节内存外,还要加上结构体BlockLink_t的大小然后做8字节对齐后导致的多申请了10字节内存。