我所处的环境是Win7 / MSVC 2010sp1,带有g版本(4.4.7、4.1.2)的两个不同的Linux盒(Red Hat)以及带有xlc(08.00.0000.0025)的AIX.
不久前,有人要求我们将一些代码从AIX移到Linux.不久之后,Linux便有所不同.通常,当信号被抛出时,我们处理它并抛出一个C异常.那没有按预期工作.
Long story short, throwing c++ exceptions from a signal handler isn’t going to work.
稍后,我整理了一个使用setjmp / longjmp的修复程序,以将异常移出处理程序.经过一些测试和验证,该功能适用于所有平台.经过强制性的三次欢乐舞蹈之后,我继续进行一些单元测试.哎呀.
我的一些测试在Linux上失败了.我观察到的是,raise函数仅工作一次.使用SIGILL进行了两次测试,第一个通过了,第二次失败了.我打开了一个斧头,开始砍掉代码以消除尽可能多的麻烦.这产生了这个较小的例子.
#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.我鼓励您使用更可靠的签名接口.第一个好处是您不再需要重置处理程序中的信号捕获…