下载 FreeRTOS
 

出色的 RTOS & 嵌入式软件

支持
最新资讯
简化任何设备的身份验证云连接。
利用 CoAP 设计节能型云连接 IoT 解决方案。
11.0.0 版 FreeRTOS 内核简介:
FreeRTOS 路线图和代码贡献流程。
使用 FreeRTOS 实现 OPC-UA over TSN。
主页|常见问题

FreeRTOS 常见问题 -
内存使用情况、启动时间&上下文切换时间

以下为常见问题节选

FreeRTOS 占用多少 RAM?
FreeRTOS 占用多少 ROM?
FreeRTOS 启动需要多久?
上下文切换时间是多少?

相关常见问题:

为什么编译器提示我 FreeRTOS 正在消耗所有可用的 RAM?
为什么队列占用这么多 RAM?
为什么编译器提示我 FreeRTOS 占用的 ROM 比您说的多得多?
如何减少 RAM 占用量?
RAM 是如何分配给任务的?
RAM 是如何分配给队列的?
任务堆栈应该多大?

以下为常见问题节选



FreeRTOS 占用多少 RAM?

这取决于您的应用程序。 以下指南基于:

  • IAR STR71x ARM7 移植。
  • 全面优化。
  • 最低配置。
  • 四种优先级。
项目 使用的字节
调度器自身 236 字节(很容易通过使用较小的数据类型降低)。
每创建一个队列,会增加 76 字节和队列存储区域(请参阅常见问题:为什么队列占用这么多 RAM?)
每创建一个任务,会增加 64 字节(包括任务名称使用的 4 个字符)和任务堆栈大小。


FreeRTOS 使用多少 ROM/Flash?

这取决于您的编译器、系统架构和 RTOS 内核配置。

如果采用的配置和常见问题“FreeRTOS 使用多少 RAM?”中描述的相同,RTOS 内核自身需要 5 到 10 KB 的 ROM 空间。



FreeRTOS 启动需要多久?

在大多数情况下 FreeRTOS、OpenRTOS 以及 SafeRTOS 以源码形式提供,用于静态链接到用户应用程序。 因此构建过程 会生成单个二进制可执行映像。 通常,这样的镜像会包括 C 启动例程以设置 C 运行时环境,之后调用 main() 执行用户应用程序。 中断向量表还将被静态配置 并包括在同一镜像的预定位置处。

下表说明了启动此类系统所需的处理引导顺序,以及 完成此处理所需的相关时间引导。 请注意,所提供的任何数字真实但仅供参考。 实际能够达到的时间 取决于所使用的架构、配置的时钟频率以及存储接口的配置。

 # 
描述
计时
1
根据要求的性能级别配置 CPU 时钟。
通常需要几次寄存器写操作,随后时钟锁定,会短暂延迟。 该过程 将需要大约几微秒,这取决于所使用的架构。

该步骤是可选的。 它可以稍后从 C 代码执行,但如果它在初始化内存之前执行,则会增加启动时间。

2
初始化仅包含 0 值 (bss) 的静态变量和全局变量。
在应用中包含 FreeRTOS 通常只会额外添加数百个写入访问, 这些访问操作在非常紧密的汇编循环中执行。 与不包括 RTOS 内核的情况相比,这将增加几微秒的时间。
3
初始化包含非零值的变量。
在应用程序中包含 FreeRTOS 通常不会为此步骤增加任何额外的时间。
4
执行其他所需的硬件设置。
通常需要在启动 RTOS 调度器之前配置外围设备。 这需要多长时间 取决于所使用外设的复杂程度, 但在 FreeRTOS 所针对的微控制器类别上,总时间通常只需要几毫秒。
5
创建应用程序队列、信号量和互斥锁。
通常,大多数队列、信号量和互斥锁会在 RTOS 调度器启动之前创建。

举例来说,在 ARM Cortex-M3 设备上,使用 ARM RVDS 编译器,并将优化设置为 1(低), 创建队列、信号量或互斥锁需要大约 500 个 CPU 周期。

6
创建应用程序任务。
通常大多数任务会在 RTOS 调度器启动之前创建。

举例来说,在 ARM Cortex-M3 设备上,使用 ARM RVDS 编译器, 并将优化设置为 1(低),创建每个任务大约需要 1100 个 CPU 周期。

7
启动 RTOS 调度器。
通过调用 vTaskStartScheduler() 启动 RTOS 调度器。 启动过程包括配置 tick 中断、创建空闲任务,然后恢复要运行的第一个任务的上下文。

举例来说,在 ARM Cortex-M3 设备上,使用 ARM RVDS 编译器,并将优化设置为 1(低), 启动 RTOS 调度器需要大约 1200 个 CPU 周期。



上下文切换时间是多少?

上下文切换时间取决于移植、编译器和配置。 在 以下测试条件下,上下文切换时间为 84 个 CPU 周期:

注意:

  • 在这些测试条件下,上下文切换时间不取决于 选择运行不同的任务还是选择继续 运行同一个任务。

  • ARM Cortex-M 移植的所有的上下文切换都在 PendSV 中断时执行。 引用的时间不包括进入中断时间。

  • 引用的时间包括一小段 C 代码。 可以确定 使用汇编代码提供完整实现,可以节省 12 个 CPU 周期 。 一般认为,维护一小段 通用 C 代码(出于维护、支持、稳健性、 追踪等自动包含的功能等原因)的好处大于 从上下文切换时间中减少 12 个 CPU 周期所带来的好处。

  • 进入中断时未自动保存的 Cortex-M CPU 寄存器 可以使用单条汇编指令保存,然后 可以使用另一个单条汇编指令再次还原。 这两个指令 自身消耗 12 个 CPU 周期。



为什么编译器提示我 FreeRTOS 正在消耗所有可用的 RAM?

示例内存分配方案中有三种方案 (由 FreeRTOS 提供)从静态分配的数组分配内存。 该数组的大小由 FreeRTOSConfig.h 中的 configTOTAL_HEAP_SIZE 常量决定。 这些只是静态分配的普通数组,因此会出现在许多工具链提供的 RAM 使用量数值中 。 实际上,工具链将 堆显示为消耗的 RAM,即使堆当时是空闲的, 因为实际上并没有为它分配内存。

C 应用程序需要一些 RAM 用于静态变量、缓冲区等。 但 它很少会使用微控制器上所有可用的 RAM。 许多 FreeRTOS 演示应用程序会指定堆的大小,以用尽所有剩余的 RAM, 看起来好像应用程序正在使用所有可用的 RAM。



为什么队列占用这么多 RAM?

队列内置了事件管理功能。 这意味着队列数据结构体包含其他 RTOS 系统会单独分配的所有 RAM 。 FreeRTOS 内部没有事件控制块的概念。



如何减少 RAM 占用量?

  • FreeRTOS-Plus-Trace 可以追踪内存分配和释放事件,因此在 分析和进而优化内存使用方面非常有用。

  • 在大多数情况下直达任务通知 可以代替二进制 信号量。 二进制信号量是需要创建的通用对象, 直达任务通知则与之不同,它直接发送到 任务,而不使用任何 RAM。

  • 事件组中的每个标志(位) 都可以用作二进制信号量, 因此可以用单个事件组替换多个二进制信号量。

  • 使用 uxTaskGetStackHighWaterMark() 函数查看哪些任务可以分配较小的堆栈。

  • 使用 xPortGetFreeHeapSize() 和 xPortGetMinimumEverFreeHeapSize() API 函数(如果可用)查看有多少已分配且未使用的 FreeRTOS 堆, 并做出相应调整

  • 如果正在使用 heap_1.c、heap_2.c、heap_4.c 或 heap_5.c,而且 应用程序的任何内容都没有直接调用 malloc()(并非 pvPortMalloc()),然后请确保 C 库的链接器未被分配堆栈,因为它永远不会 被使用。

  • 将 configMAX_PRIORITIES 和 configMINIMAL_STACK_SIZE(位于 portmacro.h 中)设置为应用程序可接受的最小值。

  • 恢复 main() 使用的堆栈。 一旦启动 RTOS 调度器,程序进入不需要使用堆栈 (除非应用程序调用仅 PC 和 Flashlite 移植直接支持的 vTaskEndScheduler(), 或像在 ARM Cortex-M 和 RX 移植中那样,使用堆栈作为中断堆栈 )。 每个任务都分配了自己的堆栈, 因此一旦 RTOS 调度器启动,分配给 main() 的堆栈可以重复使用。

  • 最小化 main() 使用的堆栈。 创建第一个应用程序任务时将自动创建空闲任务。 因此,程序进入(在 RTOS 调度器启动之前)使用的堆栈必须足够大,以便嵌套调用 xTaskCreate()(或 xTaskCreateStatic())。 手动创建空闲任务可以减少一半的堆栈要求。 手动创建空闲任务:

    1. 在 Sourcetasks.c 中找到 prvInitialiseTaskLists() 函数。
    2. 该函数在底部调用 xTaskCreate() 创建空闲任务。 从 Sourcetasks.c 中剪切此行 并将其粘贴到 main() 中。

  • 合理安排任务数量。 在下列情况下不需要空闲任务:

    1. 应用程序有一个从不阻塞的任务,并且
    2. 应用程序没有调用 vTaskDelete()。

  • 减少定义 BaseType_t 使用的数据大小(这可能会增加执行时间)。

  • 还可以进行其他小调整(例如,任务优先级队列不需要事件管理), 但深入到这种层次——会需要更多 RAM!


为什么编译器提示我 FreeRTOS 占用的 ROM 比您说的多得多?

此前引用的 ROM/Flash ROM 占用数据是真实的。 如果您编写了一个小型 FreeRTOS 测试程序,而且它似乎比预期消耗更多的 ROM, 这可能是因为构建中包含的库,而不是因为 FreeRTOS。 特别是 GCC 字符串处理库和任何浮点数库 将增加您的代码量。

FreeRTOS 在 printf-stdarg.c 文件中,包含许多字符串处理函数的精简开源实现。 在项目中包括此文件可以大大减少构建的应用程序使用的 ROM, 以及减少需要分配给任何调用字符串处理库的任务(例如 sprintf())的堆栈大小。 注意,printf-stdarg.c 开源, 但并不包含在 FreeRTOS 许可中。 在使用之前,请确保您可以接受文件本身所规定的许可条件。

此外,多数链接器会默认删除未使用的代码,但 GNU 链接器仅会在您明确告知的情况下删除未使用的代码。

查看输出的 .map 文件,找到 ROM/Flash 准确的用途。



如何向任务分配 RAM?

如果使用 xTaskCreateStatic() API 函数创建任务, 那么任务所需的 RAM 由应用程序编写者提供, 并且不会分配内存。

如果使用 xTaskCreate() API 函数创建任务, 那么任务所需的 RAM 会在 xTaskCreate() API 函数内从 FreeRTOS 堆中分配

main() 使用的堆栈不被任务使用,但可能 会被中断使用(取决于移植)。



如何向队列分配 RAM?

如果使用 xQueueCreateStatic() API 函数创建队列, 那么队列所需的 RAM 由应用程序编写者提供, 并且不会分配内存。

如果使用 xQueueCreate() API 函数创建队列, 那么队列所需的 RAM 会在 xQueueCreate() API 函数内从 FreeRTOS 堆中分配



任务堆栈应该多大?

可以使用 xTaskCreate()xTaskCreateStatic() API 函数创建任务。 函数的 usStackDepth 参数为正在创建的任务指定了分配的堆栈的大小 (换句话说,不是字节!)。 人们常会 询问如何确定 usStackDepth 的值,但是除了下面描述的一种方式之外, 在确定需要多少堆栈时, 使用 RTOS 和编写裸机应用程序( 不使用操作系统的应用程序)间没什么区别。

使用 RTOS 与编写裸机应用程序时完全相同,其所需的堆栈大小 取决于以下应用程序特定的参数:

  • 函数调用嵌套深度
  • 函数作用域变量声明的数量和大小
  • 函数参数的数量
  • 处理器架构
  • 编译器
  • 编译器优化级别
  • 中断服务程序的堆栈要求——对于许多 RTOS 移植来说是零, 这是因为 RTOS 在进入中断服务程序时会切换到专用中断堆栈 。
每当调度器暂停运行当前任务, 处理器上下文会保存该任务的堆栈中,以运行 别的任务。 下次该任务运行时,保存的处理器上下文会从该任务的堆栈中弹出 。 保存处理器上下文所需的堆栈空间 是 RTOS 自身对任务堆栈唯一的额外要求。

尽管很难确定要为任务分配多少堆栈,但 RTOS 提供了相关功能,可以通过实用的试错法调整任务堆栈大小; 实用试错法; uxTaskGetStackHighWaterMark() API 函数可以用于查看实际的堆栈使用量, 如果分配的堆栈比必要的多,则可以减小堆栈大小,并且 堆栈溢出检测 功能可以用于确定堆栈是否太小。 此外, 所有 RTOS 任务的堆栈使用量都可以使用 uxTaskGetSystemState() API 函数查看,或者有众多 FreeRTOS 感知 IDE 插件可以使用。

FreeRTOS 下载文件包含每种移植的演示应用程序,以及每个演示应用程序都提供了 FreeRTOSConfig.h 文件,其中定义了常量 configMINIMAL_STACK_SIZE。 强烈建议分配给任务的堆栈不要小于 移植演示应用程序中使用的 configMINIMAL_STACK_SIZE 设置 。

另请参阅 Erich Styger 关于使用 GNU 堆栈分析工具的博客文章



Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.