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