为什么在Linux信号处理中遇到意外的行为?

我生活在Win7 / MSVC 2010sp1,两个不同Linux版本(Red Hat),g ++版本(4.4.7,4.1.2)和AIX xlc ++(08.00.0000.0025)的环境中。

不久之前,有人要求我们将一些代码从AIX迁移到Linux。 没有太长的时间看到Linux有点不同。 通常当一个信号被抛出时,我们处理它并抛出一个C ++exception。 这没有按预期工作。

长话短说,从信号处理程序抛出c ++exception是行不通的。

有一段时间后,我将一个修复程序放在一起,使用setjmp / longjmp将exception移出处理程序。 在不同的testing和党的事情适用于所有平台。 在完成一轮立体快乐舞蹈之后,我开始进行一些unit testing。 哎呀。

我的一些testing在Linux上失败了。 我观察到的是,提升function只能工作一次。 有两个使用SIGILL的testing,第一个通过,第二个失败。 我掏出一把斧头,开始剔除代码,尽可能多地删除。 这产生了这个小例子。

#include <csetjmp> #include <iostream> #include <signal.h> jmp_buf mJmpBuf; jmp_buf *mpJmpBuf = &mJmpBuf; int status = 0; int testCount = 3; void handler(int signalNumber) { signal(signalNumber, handler); longjmp(*mpJmpBuf, signalNumber); } int main(void) { if (signal(SIGILL, handler) != SIG_ERR) { for (int test = 1; test <= testCount; test++) { try { std::cerr << "Test " << test << "\n"; if ((status = setjmp(*mpJmpBuf)) == 0) { std::cerr << " About to raise SIGILL" << "\n"; int returnStatus = raise(SIGILL); std::cerr << " Raise returned value " << returnStatus << "\n"; } else { std::cerr << " Caught signal. Converting signal " << status << " to exception" << "\n"; std::exception e; throw e; } std::cerr << " SIGILL should have been thrown **********\n"; } catch (std::exception &) { std::cerr << " Caught exception as expected\n"; } } } else { std::cerr << "The signal handler wasn't registered\n"; } return 0; } 

对于Windows和AIX盒子,我得到预期的输出。

 Test 1 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 2 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 3 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected 

对于这两个Linux盒子,看起来像这样。

 Test 1 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 2 About to raise SIGILL Raise returned value 0 SIGILL should have been thrown ********** Test 3 About to raise SIGILL Raise returned value 0 SIGILL should have been thrown ********** 

所以,我真正的问题是“ 这里发生了什么?

我的问题是:

  • 有没有人观察到这种行为?
  • 我应该怎么做才能解决这个问题?
  • 还有什么我应该注意的东西?

您必须使用sigsetjmp / siglongjmp来确保混合信号和跳转时的正确行为。 如果你改变你的代码,它将在Linux下正常工作。

您还使用了不推荐的旧信号API。 我鼓励你使用更可靠的sigaction接口。 第一个好处是你不再需要重置处理程序中的信号捕获…