51

有哪些方法可用于确定嵌入式/内存受限系统的最佳堆栈大小?如果它太大,则浪费了可以在其他地方使用的内存。但是,如果它太小,那么我们会得到这个网站的同名......

尝试快速开始:Jack Ganssle 在《设计嵌入式系统的艺术》中指出,“通过经验,人们学会了标准的、科学的方法来计算堆栈的适当大小:随机选择一个大小并希望。” 谁能做得比这更好?

要求提供更具体的示例。那么,如何使用IAR Embedded Workbench工具链在没有操作系统的情况下针对具有 2 kB RAM的MSP430 MCU的 C 程序呢?此 IDE 可以在使用 JTAG 调试器时显示堆栈内容和使用情况。

4

5 回答 5

34

确定最深堆栈使用的最常见方法是使用一些已知但不寻常的值初始化堆栈内存,然后定期(或在大型测试运行结束时)查看该模式停止的位置。

这正是 IAR IDE 确定使用的堆栈数量的方式。

于 2008-12-23T16:21:54.783 回答
23

您用静态分析标记了您的问题,但这是一个很难通过静态分析解决的问题。堆栈使用取决于程序的运行时配置文件,尤其是在使用递归或 alloca 时。鉴于这是一个嵌入式平台,我想运行pstop之类的东西并查看您的应用程序正在使用多少堆栈也很困难。

一种有趣的方法是使用当前堆栈帧的地址来确定使用了多少堆栈。您可以通过获取函数参数或局部变量的地址来做到这一点。对main函数和您认为使用最多堆栈的函数执行此操作。差异将告诉您应用程序所需的堆栈数量。这是一个示例(假设习惯上从高到低的堆栈增长)。

char *stack_top, stack_bottom;

int
main(int argc, char *argv[])
{
    stack_top = (char *)&argc;
    // ...
    printf("Stack usage: %d\n", stack_top - stack_bottom);
}

void
deeply_nested_function(void)
{
    int a;
    stack_bottom = (char *)&a;
    // ...
}

如果您的编译器允许您指定自定义函数序言(许多这样做是为了允许基于图形的程序分析),您甚至可以安排所有函数调用此类测量代码。然后你的测量功能类似于

void
stack_measurement_function(void)
{
    int a;
    stack_bottom = min(stack_bottom, (char *)&a);
    // ...
}

我使用类似于我所描述的方法来生成这些图表

于 2008-12-23T16:13:35.237 回答
5

使用好的源代码静态分析工具,您可以为您的应用程序生成调用图。鉴于此,以及编译器生成的本地/临时数量的估计,您可以直接计算堆栈需求的保守估计。

我所说的“好”分析工具是一种可以读取所有涉及的编译单元,可以确定直接函数调用,可以确定间接指针,在编译单元中,可以计算整个系统的保守点分析,可以构建考虑到点分析的调用图。这消除了许多工具,这就是为什么人们会看到诸如“在运行时填充堆栈并查看会发生什么”之类的临时方法。您还需要估计编译器在堆栈上放置的堆栈需求;您可以通过简单地了解所有类型的存储需求有多大来估算其中的大部分内容,这对于嵌入式系统 C 程序通常相当容易确定。最后,您需要相信您的应用程序没有递归调用,

DMS Software Reengineering Toolkit 满足 C 程序的所有这些要求。请参阅http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html 您仍然必须对其进行配置以通过爬取调用图并使用各种大小估计来计算堆栈需求。

如果您想要一个快速的答案,请使用堆栈填充技巧。如果您想要一个可以在每次更改源代码后重新计算的答案,您将需要静态分析方法。

于 2009-07-14T04:41:08.853 回答
2

我现在正在解决这个问题 - 即堆栈大小的分析计算。这显然将是一段高度递归的代码,因为函数调用可以将索引数组作为其一个或多个参数,并且一个或多个数组索引可能涉及函数调用!

然而,一些实现可以从复杂性中解脱出来:

(1) 使用高级语言编译器时,每条语句/代码行执行结束时的堆栈指针应与开始时的位置相同。(至少这是一个很好的规则,否则你会遇到问题!)

(2)每个函数或子程序调用返回后的堆栈指针应与调用前相同。因此,最大堆栈大小是程序中所有语句中每个语句达到的峰值堆栈大小的最大值。(至少这是一个很好的规则,否则你会遇到问题!)

当然,一条语句可以包含我上面提到的递归问题,但至少找到整个程序的最大堆栈大小要求的问题归结为找到每个语句的最大堆栈大小要求,然后选择其中的最大值。

在所有调用的函数也都被编译之前,这无法完成。因此,我为每个编译的模块生成一个文件,记录每个语句的堆栈大小(基本上是每个函数调用之前的峰值和每个函数调用之前的值(不包括由函数引起的任何未知的堆栈大小添加) call) 以及所涉及的函数名称。然后,一旦编译了所有函数,我就使用递归例程回顾性地处理这些文件,以确定峰值堆栈大小。

幸运的是,除了递归例程之外,最大可能的堆栈大小要求并不取决于程序流,尽管在典型的流(取决于数据)中,可能永远不会达到最大可能的堆栈大小。

示例:假设函数 1 调用函数 2,并且两者的程序流程都依赖于数据值 X。假设有一个 X 范围导致函数 1 执行其最差的语句,其中包括对函数 2 的调用,该语句不执行对于相同范围的 X,它是最坏情况的陈述。由于我们同时使用函数 1 和函数 2 的最坏情况来计算最大可能的堆栈大小,因此我们可能高估了堆栈大小。至少我们在安全方面犯了错误。

如果需要,我喜欢在 OS 堆栈上为中断例程提供自己的堆栈空间,因此它们不会增加程序堆栈要求,除了从中断返回的地址

于 2013-08-07T19:12:00.900 回答
-11
  • 永远不要使用递归或递归算法。(当心正则表达式库)
  • 不要使用数组,总是使用 malloc()。
  • 不要使用 alloca(),一些编译器甚至在这个函数中有 bug。

然后手动检查代码部分,寻找堆栈使用率可能最高的位置(记住我说没有数组)

  • 检查代码本身可疑 高点的堆栈使用情况,记录到调试器界面。
  • 根据估计的堆栈使用情况设置一个上限,该上限就位。例如限制服务器连接。
于 2011-12-27T22:58:00.207 回答