C嵌入式系统的堆栈和堆大小

4
我该如何确定嵌入式系统中运行的C程序的当前堆栈和堆大小?同时,我该如何发现我的嵌入式系统允许的最大堆栈和堆大小?我考虑使用逐步增加分配内存大小并调用malloc()直到无法找到堆大小的方式,但我更关心堆栈的大小。我正在使用mbed NXP LPC1768,并且正在使用名为gcc4mbed的GitHub离线编译器。有更好的想法吗?非常感谢您的帮助!

2
这将完全取决于硬件、C库和编译器工具链,因此,如所述,请阅读硬件规格。 - tbert
1
编程上如何自动获取这些信息呢? - CodeKingPlusPlus
你的硬件和软件系统是什么?你如何编译? - Basile Starynkevitch
我正在使用一款mbed NXP LPC1768,同时我在使用一个名为gcc4mbed的GitHub离线编译器。 - CodeKingPlusPlus
“离线编译器”?那不就是“编译器”吗? - tbert
我使用的mbed标准编译器是在线编译器,因此我想要明确具体地说明我正在使用什么。 - CodeKingPlusPlus
2个回答

11

要解决这个问题,请查看您的链接脚本,它将定义每个空间分配了多少。

对于堆栈大小的使用,请执行以下操作:

在启动时(在C main()之前)初始化内存时,将所有堆栈字节初始化为已知值,例如0xAA或0xCD。运行程序,在任何时候都可以停止并查看剩余的幻数值数量。如果没有看到任何幻数值,则已经溢出堆栈,可能会发生奇怪的事情。

在运行时,您还可以检查最后4个字节左右(可能是最后两个字),如果它们与您的幻数不匹配,则强制重置。这仅在系统在重置时表现良好时才有效,并且最好快速启动并且未执行“实时”或关键任务。

这是来自IAR的非常有用的白皮书


我也认为Google重定向URL不够美观。 - Kevin Vermeer

8
在运行时测量当前堆栈大小的一种简单方法是声明:
 static void* mainsp;

然后用类似以下的代码开始你的main:
 int main(int argc, char**argv) {
    int here;
    mainsp = (void*) &here;

然后在一些叶子例程中,当调用栈足够深时,执行类似的操作

    int local;
    printf ("stack size = %ld\n", 
            (long) ((intptr_t) &local - (intptr_t) mainsp));

从应用程序的完整源代码中静态估计所需的堆栈大小通常是不可判定的(考虑递归、函数指针),在实践中也非常困难(即使在一个严格受限的应用程序类别上)。可以查看Couverture。您也可以考虑使用最新的GCC编译器自定义您的插件(也许在2021年中期使用Bismon;请给我发送电子邮件至basile.starynkevitch@cea.fr),但这并不容易,并且会给出过度估计。

如果使用GCC编译,您可以使用返回地址内置函数在运行时查询堆栈帧指针。在某些体系结构上,它在某些优化标志下不可用。您还可以使用-Wstack-usage=字节大小和/或-Wframe-larger-than=字节大小警告选项来最近的GCC。
至于堆和栈空间如何分配,这取决于系统。您可以在Linux上解析/proc/self/maps文件。请参阅proc(5)。您可以使用setrlimit(2)在Linux中的用户空间限制堆栈空间。
但是要注意Rice定理

使用多线程应用程序可能会更加困难。 阅读一些Pthread教程

请注意,在简单情况下,GCC可能能够进行尾递归优化。 您可以使用gcc -Os -fverbose-asm -S foo.c编译foo.c并查看生成的foo.s汇编代码。

如果您不关心可移植性,请考虑还使用GCC的扩展的asm功能


我猜你的 &sp 实际上应该是 &here - ouah
4
您正在使用函数范围变量在堆栈上声明的事实,因此它的地址是当前堆栈指针,但您也可以使用 GCC 的显式寄存器变量扩展,并编写 char *stack_ptr asm ("sp"); 来获取对当前堆栈指针的别名。 在像mbed这样的ARM Cortex-M3上,“sp”是堆栈指针的名称,但您需要更改其他架构以使用该方法。 如果您有许多本地变量,则这将非常有用。 - Kevin Vermeer
1
您还假设数据和函数返回地址有一个统一的堆栈。我很多年前曾经处理过一个非常棘手的微处理器,其情况并非如此! - marko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接