嵌入式操作系统FreeRTOS内存管理内容

💡 原文中文,约5500字,阅读约需14分钟。
📝

内容提要

FreeRTOS从V9.0.0开始,应用程序可以完全静态分配,无需堆内存管理器。FreeRTOS提供了五个示例实现pvPortMalloc()和vPortFree(),分别是heap_1、heap_2、heap_3、heap_4和heap_5。heap_1最简单,不允许释放内存;heap_2允许释放内存,但不能合并相邻的空闲块;heap_3使用标准库函数malloc()和free(),保证线程安全;heap_4合并相邻的空闲块以避免碎片;heap_5可以跨多个不相邻的内存区域扩展堆的能力。

🔎

延伸解读

静态分配与动态分配的选择

FreeRTOS从V9.0.0开始支持完全静态分配,应用程序可以完全不使用堆内存管理器。这对于禁止动态内存分配的安全关键系统尤为重要。但静态分配需要预先知道所有内核对象的大小和数量,增加了设计复杂度。动态分配则简化了API使用,减少规划工作量,但引入了非确定性和碎片化风险。开发者应根据系统实时性要求和安全规范权衡选择。

五种堆实现的关键差异

heap_1最简单且确定性,但不支持释放,适合仅启动时创建对象的系统。heap_2允许释放但不合并空闲块,易碎片化,仅建议用于向后兼容。heap_3包装标准malloc/free,线程安全但堆大小由链接器决定。heap_4合并相邻空闲块,适合反复分配释放不同大小内存的场景。heap_5在heap_4基础上支持多个非连续内存区域,适用于内存映射不连续的系统。

碎片化与确定性的实际影响

动态内存分配的非确定性和碎片化是实时系统的主要顾虑。heap_2因不合并空闲块,在分配大小变化时容易碎片化,导致即使总空闲内存足够也无法分配。heap_4通过合并相邻块降低此风险,但仍非确定性。heap_1虽确定性且无碎片,但无法释放内存。对于反复创建删除任务且堆栈大小固定的应用,heap_2可避免碎片,但新设计更推荐heap_4。

heap_5的初始化要求

heap_5是唯一需要显式初始化的内存分配方案。在使用任何内核对象(任务、队列、信号量等)之前,必须先调用vPortDefineHeapRegions()来定义内存区域。这适用于RAM在内存映射中不连续的系统。开发者需注意初始化顺序,否则可能导致分配失败。heap_5的算法与heap_4相同,但提供了跨多个独立内存区域管理堆的能力。

❓

Q&A

FreeRTOS从哪个版本开始支持完全静态分配内存?

从V9.0.0版本开始,FreeRTOS应用程序可以完全静态分配,无需包含堆内存管理器。

FreeRTOS提供了哪几种内存分配实现?

FreeRTOS提供了五种示例实现:heap_1、heap_2、heap_3、heap_4和heap_5。

heap_1和heap_2在内存释放方面有什么区别?

heap_1不允许释放内存,而heap_2允许释放内存,但heap_2不能合并相邻的空闲块,容易产生碎片。

heap_3是如何保证线程安全的?

heap_3通过临时暂停FreeRTOS调度器来使标准库函数malloc()和free()成为线程安全。

heap_4如何避免内存碎片?

heap_4使用首次适合算法分配内存,并且会将相邻的可用内存块合并成一个较大的块,从而最大程度地减小内存碎片化风险。

heap_5适用于什么场景?

heap_5适用于系统RAM在内存映射中不是单个连续块的情况,它可以从多个不相邻的内存区域分配内存。

使用heap_5时需要注意什么?

heap_5是唯一必须在调用pvPortMalloc()之前显式初始化的内存分配方案,需要使用vPortDefineHeapRegions() API函数进行初始化,并且必须在创建任何内核对象之前调用。

🏷️

标签

➡️

继续阅读