【FreeRTOS】内存管理

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内存分配方法

特性:

  1. 适用于一旦创建好任务、信号量和队列就不在删除的应用
  2. 具有可确定性(执行所华花费的时间大多数都是一样的),而且不会导致内存碎片(因为heap_1算法无法处理内存碎片)
  3. 代码实现和内存分配过程非常简单,内存是从一个静态数组中分配到的,也就是适合于不需要动态分配内存的应用

heap_1算法没有提供内存释放函数

heap_2内存分配方法

其实与heap_1算法差不多一致,唯一的区别就是多了内存释放函数,通过引入内存块的概念和一个链表结构,来实现内存释放,该链表结构包含当前内存块的大小和指向链表中下一个空闲内存块。

特性:

  1. 可以使用在需要重复申请释放内存的应用,但是会产生内存碎片
  2. 具有不可确定性,但远比标准C中的malloc()和free()效率高

heap_3内存分配方法

特性:

  1. 需要编译器提供一个内存堆,编译器库要提供malloc()和free()函数,比如使用STM32的话可以通过修改启动文件中的Heap_Size来修改内存堆的大小
  2. 具有不确定性
  3. 可能会增加代码量(因为内部其实是调用malloc和free)

heap_3其实就是对标准C库中的malloc和free进行了简单的封装,所以才需要编译器库提供两个函数,也才会导致代码量增加

heap_4内存分配方法(常用)

heap_4其实是在heap_2的基础上进一步优化,依然使用和heap_2一样的链表结构,并额外定义两个局部静态变量xStart和pxEnd来表示链表头和尾

特性:

  1. 可以用在需要重复申请释放内存的应用中
  2. 不会产生严重的内存碎片,即使分配的内存大小是随机的
  3. 具有不确定性,但远比标准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字节内存。

### FreeRTOS内存管理实现原理 FreeRTOS内存管理主要通过动态内存分配机制完成,该机制允许任务、队列和其他对象在运行时按需申请和释放内存。这种灵活性对于嵌入式系统的开发至关重要[^1]。 #### 动态内存分配方法 FreeRTOS 提供了四种不同的动态内存分配方案,开发者可以根据具体需求选择合适的策略: 1. **标准 C 库 malloc 和 free 函数** 使用标准库函数 `malloc` 和 `free` 进行动态内存分配。这种方式简单易用,但由于依赖外部库,在某些资源受限的环境中可能不适用。 2. **pvPortMalloc 和 vPortFree 自定义接口** FreeRTOS 定义了自己的内存分配接口 `pvPortMalloc` 和 `vPortFree`,这些接口可以被重写以适配特定硬件平台的需求。此方法通常用于优化性能或减少堆碎片化。 3. **固定大小块分配器 (Block allocator)** 此种方法将整个可用堆划分为若干个相同大小的小块,并从中分配给请求者。这种方法的优点在于快速且无碎片风险;缺点则是可能导致浪费空间如果大多数分配不符合预设尺寸的话。 4. **多分区分配器 (MPU-safe allocator)** 针对具有存储保护单元(MPU)的安全环境设计的一种更复杂的分配算法。它允许多个独立区域存在并支持不同权限设置下的安全访问控制。 以下是基于 pvPortMalloc/vPortFree 接口的一个典型实现例子: ```c void *pvPortMalloc( size_t xWantedSize ) { void *pReturn; pReturn = malloc( xWantedSize ); configASSERT( pReturn ); // Debugging assertion to catch memory allocation failures. return pReturn; } // Corresponding free function. void vPortFree( void *pv ) { free(pv); } ``` 上述代码片段展示了如何使用标准C库中的 `malloc()` 和 `free()` 来实现自定义版本的 `pvPortMalloc` 及 `vPortFree` 函数。 #### 最佳实践建议 为了提高效率并降低错误发生率,在实际项目中应遵循以下几点最佳实践: - 始终检查返回值是否成功,尤其是在调用 `pvPortMalloc` 后确认指针非 NULL。 - 尽量避免频繁地进行小型短期对象的创建销毁操作以免引起过多开销或者造成内存泄漏等问题。 - 如果目标设备有严格限制,则考虑采用静态而非动态的方式预先分配所需的所有缓冲区等资源。 - 对于长时间运行的应用程序来说,定期监控剩余自由内存数量有助于及时发现潜在隐患。 - 当选用 block 类型分配器时,请合理规划各区块容量分布以便满足大部分常见场景下所需的多样性同时兼顾整体利用率最大化的目标。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值