我目前正从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,
... )
下一次迭代时停止。
我在这里采取了错误的方法,还是有更好的方法?
答案 0 :(得分:2)
这在Java中与在c ++中具有相同的问题。你是过度承诺的堆栈。
一些递归算法在堆栈上的运行效率高于堆栈。
确实,他们通常是分而治之的类型。
递归的有用性是将计算减少到每次调用更易于管理的计算。 limit - 1
不是这样的候选人。
如果您的问题仅与信号有关,我很遗憾无法就您的系统提供任何建议。
答案 1 :(得分:1)
您的信号处理程序无法解决堆栈溢出问题。设置interrupted
标志无济于事。当您的信号处理程序返回时,尝试写入堆栈末尾之外的地址的指令将恢复,并且它仍将尝试写入超出堆栈末尾的位置。您的代码无法返回检查interrupted
标记的部分。
非常谨慎和许多特定于体系结构的代码,您的信号处理程序可能会更改遇到信号的线程的上下文,这样当它恢复时,它将处于代码中的不同点。
您还可以使用setjmp()
和longjmp()
以更粗略的粒度完成此操作。
另一种方法是使用pthread_attr_setstackaddr()
之前的pthread_attr_setstacksize()
和pthread_create()
来设置线程以使用代码分配的堆栈。您将在该辅助线程中运行您的代码而不是主要代码。您可以使用mprotect()
将分配的最后一页或两个堆栈设置为不可写。然后,您的信号处理程序可以设置interrupted
标志,并将这些页面设置为可写。这应该给你足够的空间,使得恢复的代码可以在不重新提升信号的情况下执行,远远足以检查标志,并优雅地返回。请注意,这是一次性的最后手段,除非您能找到一个好点,将这些保护页面设置为不可写。