处理嵌入式系统中的堆栈溢出

时间:2009-07-27 00:46:51

标签: error-handling embedded stack-overflow

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

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

4 个答案:

答案 0 :(得分:12)

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

  1. 静态分析(使用工具)
  2. 使用完整的代码覆盖率运行代码时的堆栈使用情况的测量(或尽可能高的代码覆盖率,直到您确定堆栈使用的程度是合理的,只要您很少运行的代码不使用比正常执行路径更多的堆栈)
  3. 但即使如此,您仍然希望有一种检测的方法,然后处理堆栈溢出(如果可能的话),以获得更强大的功能。这在项目开发阶段尤其有用。 检测溢出的一些方法:

    1. 如果处理器支持内存读/写中断(即内存访问断点中断),则可以将其配置为指向堆栈区域的最远范围。
    2. 在内存映射配置中,设置一个小的(或大的)RAM块,它是一个“堆栈保护”区域。用已知值填充它。在嵌入式软件中,定期(尽可能经常)检查该区域的内容。如果它发生变化,则假定堆栈溢出。
    3. 一旦检测到它,您就需要处理它。我不知道代码可以从堆栈溢出中优雅地恢复的许多方法,因为一旦发生这种情况,您的程序逻辑几乎肯定会失效。所以你能做的就是

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

答案 1 :(得分:2)

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

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

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

您还应该考虑在堆栈后立即放置一个空的RAM缓冲区,这样如果您确实检测到溢出,则可以关闭系统而不会丢失数据。如果您的堆栈立即被堆或SRAM数据跟踪,那么识别溢出​​将意味着您已经遭受了损坏。你的缓冲区会保护你一段时间。在32位微处理器上,你应该有足够的RAM来提供至少一个小缓冲区。

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:1)

如果您使用的是带有内存管理单元的处理器,则您的硬件可以在最小的软件开销下为您完成此操作。大多数现代32位处理器都有它们,并且也有越来越多的32位微控制器。

在MMU中设置将用于堆栈的存储区。它应该与MMU不允许访问的两个存储区域接壤。当您的应用程序运行时,一旦溢出堆栈,您将收到异常/中断。

因为您在发生错误时遇到异常,所以您确切知道应用程序中堆栈变坏的确切位置。您可以查看调用堆栈,以确切了解自己的位置。这使得找到问题变得容易,而不是通过在问题发生很久之后发现问题来找出问题所在。

我在PPC和AVR32处理器上成功使用了它。当你开始使用MMU时,你觉得这是浪费时间,因为你相处很多没有它很多年但是一旦你在你的记忆问题出现的确切位置看到异常的优点,你将永远不会回去。如果您不允许内存访问ram的底部公园,MMU也可以检测到零指针访问。

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

除此之外,带有MMU的处理器通常也有很多内存,你的程序不太可能溢出堆栈,你不需要微调所有内容,让你的应用程序能够以小内存正确运行足迹。

另一种方法是使用处理器调试工具在内存中引入中断访问堆栈末尾。这可能是非常特定于处理器的。

相关问题