【FreeRTOS 教程 五】FreeRTOS 内存管理细致讲解

目录

一、FreeRTOS 堆内存管理:

二、RTOS源代码内存分配实现:

(1)heap_1.c:

(2)heap_2.c:

(3)heap_3.c:

(4)heap_4.c:

(5)heap_5.c:

三、堆栈使用和堆栈溢出检查:

(1)堆栈使用:

(2)堆栈溢出检测(方法 1): 

(3)堆栈溢出检测(方法 2): 

(4)堆栈溢出检测(方法 3): 

四、静态内存分配 vs 动态内存分配:

(1)使用动态分配的 RAM 创建 RTOS 对象:

(2)使用静态分配的 RAM 创建 RTOS 对象:

五、静态分配的 FreeRTOS 项目注意事项: 

(1)configSUPPORT_STATIC_ALLOCATION宏定义常量:

(2)configSUPPORT_DYNAMIC_ALLOCATION宏定义常量:

六、FreeRTOS教程示例代码下载:


一、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 了解类似实现,该实现将空间的内存块组合成单一的大内存块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

The_xzs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值