如何使用堆栈但避免C ++中的堆栈溢出

时间:2015-10-05 22:22:31

标签: c++ macos stack-overflow exc-bad-access

我目前正从Java迁回C ++。在某些C ++领域,可以通过在堆栈上进行更多计算来实现更高的性能。并且一些递归算法在堆栈上的运行效率高于堆栈。

显然堆栈是一种资源,如果我要使用它,我应该确保我不会消耗太多(以至于崩溃我的程序)。

我正在运行Xcode,并编写了以下简单程序:

#include <csignal>
static bool interrupted = false;

long stack_test(long limit){
    if((limit>0)&&(interrupted==false))
       return stack_test(limit-1)+1; // program crashes here with EXC_BAD_ACCESS...
    else
        return 0;
}

void signal_handler(int sig){
    interrupted = true;
}

int main(char* args[]){
    signal(SIGSEGV,&signal_handler);
    stack_test(1000000);
    signal(SIGSEGV,SIG_DFL);
}

文档说明在BSD上运行,可以使用getrlimit()检查堆栈限制,并且当达到堆栈限制时,会发出SIGSEGV事件。我尝试为此事件安装上述事件处理程序,但我的程序在EXT_BAD_ACCESS (code=2, ... )下一次迭代时停止。

我在这里采取了错误的方法,还是有更好的方法?

2 个答案:

答案 0 :(得分:2)

这在Java中与在c ++中具有相同的问题。你是过度承诺的堆栈。

  

一些递归算法在堆栈上的运行效率高于堆栈。

确实,他们通常是分而治之的类型。 递归的有用性是将计算减少到每次调用更易于管理的计算。 limit - 1不是这样的候选人。

如果您的问题仅与信号有关,我很遗憾无法就您的系统提供任何建议。

答案 1 :(得分:1)

您的信号处理程序无法解决堆栈溢出问题。设置interrupted标志无济于事。当您的信号处理程序返回时,尝试写入堆栈末尾之外的地址的指令将恢复,并且它仍将尝试写入超出堆栈末尾的位置。您的代码无法返回检查interrupted标记的部分。

非常谨慎和许多特定于体系结构的代码,您的信号处理程序可能会更改遇到信号的线程的上下文,这样当它恢复时,它将处于代码中的不同点。

您还可以使用setjmp()longjmp()以更粗略的粒度完成此操作。

另一种方法是使用pthread_attr_setstackaddr()之前的pthread_attr_setstacksize()pthread_create()来设置线程以使用代码分配的堆栈。您将在该辅助线程中运行您的代码而不是主要代码。您可以使用mprotect()将分配的最后一页或两个堆栈设置为不可写。然后,您的信号处理程序可以设置interrupted标志,并将这些页面设置为可写。这应该给你足够的空间,使得恢复的代码可以在不重新提升信号的情况下执行,远远足以检查标志,并优雅地返回。请注意,这是一次性的最后手段,除非您能找到一个好点,将这些保护页面设置为不可写。