目录
(1)configSUPPORT_STATIC_ALLOCATION宏定义常量:
(2)configSUPPORT_DYNAMIC_ALLOCATION宏定义常量:
一、FreeRTOS 堆内存管理:
每次创建任务、队列、互斥锁、软件定时器、信号量或事件组时,RTOS 内核都需要 RAM。RAM 可以从 RTOS API 对象创建函数内的 RTOS 堆自动动态分配,或者可以由应用程序编写者提供。
如果 RTOS 对象是动态创建的,那么标准 C 库 malloc() 和 free() 函数有时可用于此目的,但是往往存在以下问题:
- 它们在嵌入式系统上并不总是可用,
- 它们占用了宝贵的代码空间,
- 它们不是线程安全的,而且
- 它们不是确定性的(执行函数所需时间将因调用而异)
所以往往需要一个替代的内存分配实现。
一个嵌入式/实时系统的 RAM 和定时要求可能与另一个非常不同,所以单一的分配算法 RAM 将永远只适用于一个应用程序子集。
为了避免此问题,FreeRTOS 将内存分配 API 保留在其可移植层。可移植层在实现核心 RTOS 功能的源文件之外,允许提供适合于正在开发的实时系统的特定应用程序实现。当 RTOS 内核需要 RAM 时,它不调用 malloc(),而是调用 pvPortMalloc()。释放 RAM 时,RTOS 内核调用 vPortFree(),而不是 free()。
FreeRTOS 提供了几种堆管理方案,其复杂性和功能各不相同。开发者也可以提供自己的堆实现,甚至同时使用两个堆实现。同时使用两个堆实现允许将任务堆栈和其他 RTOS 对象放置在内部 RAM 中,并将应用程序数据放置在较慢的外部 RAM 中。
二、RTOS源代码内存分配实现:
FreeRTOS 下载包含五个内存分配实现示例,下面介绍所提供的每个实现方式何时选择可能最合适。
每个提供的实现都包含在单独的源文件中(分别是 heap_1.c、heap_2.c、heap_3.c、heap_4.c 和 heap_5.c),位于主 RTOS 源代码下载内容的 Source/Portable/MemMang 目录下。可根据需要添加其他实现方式。每次一个项目中,只应包含其中一个源文件[这些可移植层函数定义的堆将由 RTOS 内核使用,即使用 RTOS 的应用程序选择使用自己的堆实现]。
以下是介绍:
- heap_1 —— 最简单,不允许释放内存。
- heap_2 —— 允许释放内存,但不会合并相邻的空闲块。
- heap_3 —— 简单包装了标准 malloc() 和 free(),以保证线程安全。
- heap_4 —— 合并相邻的空闲块以避免碎片化。包含绝对地址放置选项。
- heap_5 —— 如同 heap_4,能够跨越多个不相邻内存区域的堆。
注意:
- heap_1 不太有用,因为 FreeRTOS 添加了静态分配支持。
- heap_2 现在被视为旧版,因为较新的 heap_4 实现是首选。
(1)heap_1.c:
heap_1 不太有用,因为 FreeRTOS 添加了静态分配支持。
- heap_1 是最简单的实现方式。内存一经分配,它不允许内存再被释放。尽管如此,heap_1.c 还是适用于大量嵌入式应用程序。这是因为许多小型和深度嵌入的应用程序在系统启动时,创建了所需的所有任务、队列、信号量等,并在程序的生命周期内使用所有这些对象(直到应用程序再次关闭或重新启动)。任何内容都不会被删除。
- 这个实现只是在要求使用 RAM 时将一个单一的数组细分为更小的块。数组的总大小(堆的总大小)通过 configTOTAL_HEAP_SIZE(定义于 FreeRTOSConfig.h 中)设置。提供了 configAPPLICATION_ALLOCATED_HEAP FreeRTOSConfig.h 配置常量,以允许将堆放置在内存中的特定地址。
- xPortGetFreeHeapSize() API 函数返回未分配的堆空间总量,允许优化 configTOTAL_HEAP_SIZE 设置。
heap_1 实现:
- 如果您的应用程序从未删除任务、队列、信号量、互斥锁等,则可以使用。(这实际上涵盖了使用 FreeRTOS 的大多数应用程序)。
- 始终具有确定性(总是需要相同的时间来执行),不会导致内存碎片化。
- 非常简单,且从静态分配的数组分配内存,这意味着它通常适合用于不允许真实动态内存分配的应用程序。
(2)heap_2.c:
heap_2 现在被视为旧版,因为 heap_4 是首选。
- heap_2 使用最佳适应算法,并且与方案 1 不同,它允许释放先前分配的块,它不将相邻的空闲块组合成一个大块。有关不合并空闲块的实现,请参阅 heap_4.c。
- 可用堆空间的总量通过 configTOTAL_HEAP_SIZE(定义于 FreeRTOSConfig.h 中)设置。提供了 configAPPLICATION_ALLOCATED_HEAP FreeRTOSConfig.h 配置常量,以允许将堆放置在内存中的特定地址。
- xPortGetFreeHeapSize() API 函数返回未分配的堆空间总量,(允许优化 configTOTAL_HEAP_SIZE 设置),但不提供关于未分配的内存如何被碎片化成小块的信息。
- pvPortCalloc() 函数的签名与标准库 calloc 函数相同。它为一个对象数组分配内存,并将分配的存储空间中的所有字节初始化为零。如果分配成功,它会返回指向分配的内存块中最低字节的指针。如果分配失败,它会返回一个空指针。
此实现介绍:
即使应用程序重复删除任务、队列、信号量、互斥锁等,仍然可用,但要注意以下关于内存碎片化的信息。
如果正在分配和释放的内存为随机大小,则不应使用。例如:
- 如果应用程序动态地创建和删除任务,且分配给正在创建任务的堆栈大小总是相同的,那么 heap2.c 可以在大多数情况下使用。但是,如果分配给正在创建任务的堆栈的大小不是总相同,那么可用的空间内存可能会被碎片化成许多小块,最终导致分配失败。这种情况下,heap_4.c 是更好的选择。
- 如果应用程序动态地创建和删除任务,且队列存储区域在每种情况下都是相同的(队列存储区域是队列项大小乘以队列长度),那么 heap_2.c 可以在大多数情况下使用。但是,如果在每种情况下的队列存储区域不相同,那么可用的空闲内存可能会被碎片化成许多小块,最终导致分配失败。这种情况下,heap_4.c 是更好的选择。
- 应用程序直接调用 pvPortMalloc() 和 vPortFree(),而不是仅通过其他 FreeRTOS API 函数间接调用。
- 如果您应用程序的队列、任务、信号量、互斥锁等的顺序不可预测,可能会导致内存碎片化。这对几乎所有的应用程序来说都是不可能的,但应牢记这一点。
- 非确定性,但比大多数标准 C 库 malloc 实现更有效。
heap_2 适用于许多必须动态创建对象的小型实时系统。可参考heap_4 了解类似实现,该实现将空间的内存块组合成单一的大内存块。