8

在嵌入式软件中,如何以通用方式处理堆栈溢出?我遇到了一些像最近的 AMD 处理器一样以硬件方式保护的处理器。维基百科上有一些技术,但这些是真正实用的方法吗?

任何人都可以给出一个明确的建议方法,该方法适用于当今的 32 位嵌入式处理器吗?

4

4 回答 4

13

理想情况下,您使用静态堆栈使用(无递归调用)编写代码。然后,您可以通过以下方式评估最大堆栈使用量:

  1. 静态分析(使用工具)
  2. 在运行具有完整代码覆盖率的代码时测量堆栈使用率(或尽可能高的代码覆盖率,直到您有合理的信心确定堆栈使用的范围,只要您很少运行的代码不使用特别多的堆栈比正常的执行路径)

但即便如此,您仍然希望有一种方法来检测处理堆栈溢出(如果可能的话),以提高鲁棒性。这在项目的开发阶段尤其有用。一些检测溢出的方法:

  1. 如果处理器支持内存读/写中断(即内存访问断点中断),则可以将其配置为指向堆栈区域的最远范围。
  2. 在内存映射配置中,设置一个小的(或大的)RAM 块作为“堆栈保护”区域。用已知值填充它。在嵌入式软件中,定期(尽可能频繁地)检查该区域的内容。如果它发生变化,假设堆栈溢出。

一旦你检测到它,那么你需要处理它。我不知道代码可以从堆栈溢出中优雅地恢复的许多方法,因为一旦发生,您的程序逻辑几乎肯定会失效。所以你能做的就是

  1. 记录错误
    1. 记录错误非常有用,因为否则症状(意外重新启动)可能很难诊断。
    2. 警告:即使在堆栈损坏的情况下,日志记录例程也必须能够可靠地运行。例程应该很简单。即堆栈损坏,您可能无法尝试使用您花哨的 EEPROM 写入后台任务写入 EEPROM。也许只是将错误记录到为此目的而保留的结构中,在非初始化 RAM 中,然后可以在重新启动后检查。
  2. 重新启动(或者可能关闭,特别是如果错误重复出现)
    1. 可能的替代方案:仅重新启动特定任务,如果您使用的是 RTOS,并且您的系统设计为隔离堆栈损坏,并且所有其他任务都能够处理该任务重新启动。这需要一些认真的设计考虑。
于 2009-07-27T02:40:35.950 回答
2

虽然嵌入式堆栈溢出可能是由递归函数失控引起的,但也可能是由错误的指针使用引起的(尽管这可能被认为是另一种类型的错误),以及堆栈大小不足的正常系统操作。换句话说,如果您不分析您的堆栈使用情况,它可能会发生在缺陷或错误情况之外。

在您可以“处理”堆栈溢出之前,您必须识别它。这样做的一个好方法是在初始化期间使用模式加载堆栈,然后监视在运行时有多少模式消失了。通过这种方式,您可以识别堆栈已达到的最高点。

模式检查算法应该在堆栈增长的相反方向上执行。因此,如果堆栈从 0x1000 增长到 0x2000,那么您的模式检查可以从 0x2000 开始以提高效率。如果您的模式是 0xAA 并且 0x2000 处的值包含 0xAA 以外的内容,那么您知道您可能有一些溢出。

您还应该考虑在堆栈之后立即放置一个空的 RAM 缓冲区,以便在检测到溢出时可以关闭系统而不会丢失数据。如果您的堆栈后面紧跟着堆或 SRAM 数据,那么识别溢出​​将意味着您已经遭受了损坏。您的缓冲区将保护您更长的时间。在 32 位 micro 上,您应该有足够的 RAM 来提供至少一个小缓冲区。

于 2009-07-27T01:52:08.370 回答
2

如果您使用的是带有内存管理单元的处理器,您的硬件可以为您做到这一点,而软件开销最小。大多数现代 32 位处理器都有它们,越来越多的 32 位微控制器也具有它们。

在 MMU 中设置将用于堆栈的内存区域。它的边界应该是两个 MMU 不允许访问的内存区域。当您的应用程序运行时,您将在堆栈溢出时立即收到异常/中断。

因为你在错误发生的那一刻得到了一个异常,所以你确切地知道在你的应用程序中堆栈出错的地方。您可以查看调用堆栈以准确了解您是如何到达当前位置的。这比通过在问题发生很久之后才检测到问题来找出问题所在更容易找到问题。

我已经在 PPC 和 AVR32 处理器上成功使用了它。当您开始使用 MMU 时,您会觉得这是在浪费时间,因为多年来您在没有它的情况下相处得很好,但是一旦您在发生内存问题的确切位置看到异常的优势,您将永远不会回头。如果您禁止对 ram 底部公园的内存访问,MMU 也可以检测零指针访问。

如果您使用的是 RTOS,您的 MMU 会保护内存,并且一个任务中的其他任务错误不应影响它们。这意味着您还可以轻松地重新启动任务,而不会影响其他任务。

除此之外,带有 MMU 的处理器通常也有很多内存,您的程序溢出堆栈的可能性要小得多,而且您不需要微调所有内容以使您的应用程序以较小的内存占用正确运行。

对此的替代方法是使用处理器调试工具来导致对堆栈末尾的内存访问中断。这可能是非常特定于处理器的。

于 2009-07-27T06:24:34.980 回答
1

堆栈溢出发生堆栈内存因调用堆栈太大而耗尽?例如,递归函数的层次太深。

有一些技术可以通过在堆栈之后放置已知数据来检测堆栈溢出,以便可以检测堆栈是否增长过多并覆盖它。

有静态源代码分析工具,例如 GnatStack、AbsInt 的 StackAnalyzer 和 Bound-T,可用于确定或猜测最大运行时堆栈大小。

于 2009-07-27T01:33:49.663 回答