最近我开始学习 ThreadX RTOS,我注意到在链接器脚本中,并crt0.S
为 Cortex-M4 提供了 gcc 工具链,.stack
并且.heap
段的大小分别分配为 1024 字节和 128 字节。
在编写了一个在字节池上静态创建 2 个线程的简单程序后,我运行了objdump -t program.elf
. 原来,为字节池、线程控制块和其他ThreadX变量、指针分配的内存都在该.bss
节中。
.stack
我想知道创建和分区的目的是什么.heap
。它们是否存在以防调用动态内存分配函数(例如malloc
C 标准库(newlib)中的动态内存分配函数)?
.stack 内存是线程使用的堆栈
main()
,在 Cortex-M 上至少也由中断处理程序使用。这是系统堆栈。一旦调度程序启动,主线程就完成了,但是堆栈需要足够大以支持任何启动初始化以及最坏情况下的中断处理程序嵌套。
在某些情况下,在线程中尽可能少地执行操作
main
以最小化堆栈需求可能是一个好主意,并使用创建其他线程的初始“根线程”启动调度程序,然后终止并恢复其堆栈空间,或者执行一些有用的后台进程。需要
.heap
支持动态内存分配。Newlibprintf
和相关函数以及其他函数在strtok
使用动态内存时很糟糕,因此如果您调用这些函数,则需要它,并且大多数 RTOS(不太熟悉 ThreadX)都提供动态分配线程堆栈的选项。默认堆分配器具有不确定的计时特征,因此最好在硬实时线程中避免使用。
另请注意, Newlib 中的
malloc
/free
默认情况下不是线程安全的。ThreadX 或您的工具链可能已经提供了它们,但您需要确保 sys/locks.h 中声明的函数具有合适的覆盖(例如使用互斥体)。但要小心;我已经看到供应商的一些糟糕的实现,他们应该更好地了解通过禁用中断和/或挂起调度程序将 malloc/free 视为关键部分来实现锁。这会影响每个任务的实时性能和行为,无论是否使用 malloc。使用互斥体将影响仅限于那些使用动态分配的任务,根据建议,这些任务在任何情况下都不应该是硬实时部分。
通常,建议在内存受限、安全关键和实时系统中避免动态内存分配。在这种情况下,您还必须避免诸如
printf
、sprintf
et al之类的调用,这些调用通常也非常需要堆栈。如果减少“tiny printf”实现,您可以使用任意数量的实现。如果从 Newlib 中删除sbrk
和sbrk_r
syscall 实现,则任何使用 malloc 的尝试都将无法链接。但如果例如 ThreadX 依赖它,这可能就没那么容易了。您可能别无选择,只能提供至少一些堆。如果您的应用程序需要确定性内存管理,那么您应该使用它为此提供的任何服务,例如 ThreadX 字节池或块池。对于没有本机支持的 RTOS,实现块池很简单 - 您只需创建一个指向内存块的指针的 RTOS 队列(在示例中的静态数组中),然后从队列中获取进行分配并返回到队列进行释放。