FreeRTOS 提供了多种动态内存分配方案,这些方案通过不同的内存管理器(heap managers)实现,主要位于 FreeRTOS/Source/portable/MemMang
目录下。以下是几种常见的动态内存分配方案:
heap_1
特点:
- 简单性:
heap_1
是所有FreeRTOS内存管理方案中最简单的一个。它的设计目标是在资源受限的嵌入式系统中提供基本的动态内存分配功能。 - 不可释放内存:与其他几种内存管理方案不同,
heap_1
分配出去的内存空间一旦分配完成,就不支持再次释放。这意味着一旦任务、信号量、消息队列等分配了内存,这部分内存将永久占用,直到系统重启。 - 单块分配:它维护一个连续的内存块,当有内存分配请求时,直接从这个内存块中切割出所需大小的空间。这种方式不涉及复杂的内存碎片管理。
- 低开销:因为其简单的设计,
heap_1
的运行时开销相对较小,适合那些对内存管理开销非常敏感的应用场景。
工作原理:
- 初始化:在系统启动时,
heap_1
会初始化一个大的静态内存数组(通常称为 ucHeap[]),这个数组构成了整个可分配内存的池。 - 分配过程:当任务需要内存时,
heap_1
会检查 ucHeap[] 中是否有足够大的连续空间来满足请求。如果有,它就从这片连续空间中分配出所需的内存,并更新剩余内存的记录。 - 无释放操作:一旦分配,
heap_1
不支持通过函数调用来释放已分配的内存。这意呀着开发者必须谨慎地规划内存的使用,避免不必要的分配,以免过早耗尽内存资源。
使用场景:
由于 heap_1
的特性,它最适合那些对内存管理要求不高、内存分配模式可预测、且不需要频繁释放内存的应用。例如,某些简单的嵌入式系统、原型开发或对实时性要求极高而对内存灵活性要求较低的场合。
heap_2
特点:
- 多尺寸管理:
heap_2
引入了对不同尺寸内存块的管理。它将可用内存分割成多个链表,每个链表负责管理特定大小的内存块。这有助于减少内存碎片,提高内存分配和回收的效率。 - 支持内存释放:与
heap_1
不同,heap_2
支持动态地分配和释放内存。当任务不再需要一块内存时,可以通过调用释放函数将内存归还给相应的链表,使得这块内存可以被后续的分配请求重复利用。 - 减少碎片:通过将内存按大小分类管理,
heap_2
可以更有效地复用内存块,减少因频繁分配和释放不同大小内存而产生的碎片问题。
工作原理:
- 初始化:初始化时,
heap_2
会将整个内存池分割成多个预定义大小的内存块,并将这些块分别链接到对应的链表中。 - 分配过程:当有内存分配请求时,
heap_2
会遍历链表,寻找第一个足够大的内存块。如果找到,该块将从链表中移除并返回给请求者。如果找不到合适大小的块,且系统配置允许,可能会从更大的块中分割出所需大小的块,然后将剩余部分放回相应链表。 - 释放过程:释放内存时,该内存块会根据其大小被放回到正确的链表中。这样,相同大小的块可以被高效复用。
使用场景:
heap_2
适用于那些需要动态分配和释放内存、并且对内存使用效率有一定要求的嵌入式系统。它特别适合于存在多种不同大小内存需求的应用场景,通过减少内存碎片,提高了内存利用率,降低了内存分配失败的风险。然而,相较于 heap_1
,heap_2
的管理逻辑更为复杂,可能带来一定的运行时开销。因此,在选择是否使用 heap_2
时,需要权衡系统对内存管理灵活性和效率的需求与额外开销之间的关系。
heap_3
特点:
- 依赖宿主环境:与 FreeRTOS 内置的其他内存管理方案不同,
heap_3
不直接管理内存,而是调用 C 标准库的内存管理函数。这意味着它依赖于编译器或宿主机操作系统提供的内存管理实现。 - 简单集成:由于直接利用现有的内存管理接口,
heap_3
的集成相对简单,无需深入了解复杂的内存分配算法或数据结构。 - 功能全面:因为使用标准库的
malloc()
和free()
,理论上支持任意大小的内存块分配和释放,具有很高的灵活性。
使用考量:
- 性能和开销:虽然使用方便,但
heap_3
的性能和开销取决于宿主环境的内存管理实现。在一些资源受限的嵌入式系统中,标准库的内存管理可能不够高效,引入额外的开销或延迟。 - 兼容性和移植性:由于依赖于外部内存管理函数,
heap_3
的行为可能随编译器或平台的不同而有所变化,影响系统的兼容性和移植性。 - 实时性:标准库的内存管理函数往往不是为硬实时系统设计的,可能无法保证严格的时序要求,这对于某些对时间敏感的嵌入式应用可能不适宜。
适用场景:
- 对于快速原型开发或评估阶段,开发者可能优先考虑使用
heap_3
,因为它简化了内存管理的实现,便于快速测试其他系统功能。 - 当项目运行在拥有高效内存管理机制的宿主环境,且实时性要求不高时,
heap_3
也是一个可行的选择。 - 对于需要与宿主系统(如桌面操作系统上的模拟器或测试环境)紧密集成的开发场景,使用
heap_3
可以减少自定义内存管理代码的工作量。
Heap_4
是 FreeRTOS 提供的动态内存分配方案之一。FreeRTOS 支持多种内存分配策略,以适应不同应用场景的需求, Heap_4 是这些策略中的一种实现。下面简要介绍 Heap_4 的特点和工作原理:
Heap_4
特点:
-
简单高效:Heap_4 实现相对简单,它通过维护一个或多个内存块链表来管理可用的内存空间。这些内存块按照大小排序,当有新的内存分配请求时,它会从链表中寻找最合适的块进行分配。
-
固定块大小分配:与 Heap_1、Heap_2 和 Heap_3 不同,Heap_4 并不直接支持任意大小的内存分配请求。它更适合于那些内存需求较为固定、或者可以预先确定几种常见大小的情况。开发者需要预先定义好几种不同大小的内存块池,每个池维护一个链表。
-
减少碎片:通过限制分配的内存块大小种类,Heap_4 有助于减少内存碎片问题。特别是在那些频繁分配和释放固定大小对象的应用场景中,能够更有效地复用内存块,避免小碎片积累导致的大块内存无法分配的问题。
-
配置灵活性:用户可以根据应用的具体需求,配置不同的内存块大小和数量,以达到最佳的内存使用效率和性能平衡。
工作原理:
- 初始化:在系统启动时,Heap_4 会被初始化,此时会根据配置好的内存块大小划分整个可用的内存区域,为每个大小创建一个或多个内存块链表。
- 分配内存:当任务需要分配内存时,Heap_4 会在对应大小的链表中查找是否有空闲的内存块。如果有,则从中取出一个分配给请求者;如果没有足够大的块,且配置允许,可能会尝试分裂大块以满足请求,但这通常不在 Heap_4 的直接功能范围内,更多依赖于如何预先配置内存池。
- 释放内存:释放内存时,该内存块会被重新链接到对应大小的链表中,等待下一次分配使用。