2013-01-24 38 views
7

我有一个C++应用程序,有很多线程,其中大部分都是32k的堆栈大小。问题是,有时我得到一个stackoverflow,我想检测哪个线程导致了stackoverflow并将其写入日志文件,问题是我不能陷入它。如何用pthread捕获堆栈溢出?

我读了关于SIGSEGV,我只能在没有线程的情况下捕获这个信号。我也尝试使用pthread_sigmask()并使用libsigsegv,但我也失败了。

任何人都可以告诉我一个关于在线程中发生堆栈溢出时捕获SIGSEGV的小例子吗?

回答

8

多线程应用程序的陷阱堆栈溢出在大多数情况下与单线程应用程序无关。它可能不同的主要方式是如果你大幅溢出,为主线程;在大多数情况下,这仍然会给你一个无效的堆栈指针和SIGSEGV,但是对于小线程堆栈,溢出可能会将堆栈指针置于另一个线程堆栈的中间,在这种情况下会发生非常糟糕的事情,并且没有希望推进进展。如果您使用的是小堆栈,您应该检查的另一个问题是您没有禁用防护页。使用pthread_attr_setstack(顺便说一句,这个函数不推荐使用),除非你已经自己设置它们,否则不会为你提供警戒页面。使用pthread_attr_setstacksize(适当的现代界面)不应该干扰防护页分配,但如果您认为溢出大幅度增加,您可能需要增加防护尺寸(使用pthread_attr_setguardsize)。

即便如此,处理堆栈溢出的一般过程是为SIGSEGV设置处理程序并将其设置为在备用堆栈上运行。最后一点非常重要!由于在生成信号时堆栈指针无效,因此需要为信号处理程序本身运行一个备用堆栈。 标记关于给定信号是否在备用堆栈上处理是每个信号属性(由sigaction设置),实际备用堆栈是每个线程属性。 每个线程都必须使用sigaltstack设置自己的备用堆栈。备用堆栈的空间可以通过mallocmmap进行分配,但最简单的方法是在线程启动函数中创建一个大小约为2-4k的自动char数组,并将其用于备用堆栈。基本上,这相当于在线程堆栈的底部处预留一个小范围,以便在堆栈顶部溢出后用作信号处理程序的紧急堆栈空间。