为什么我会遇到Linux信号处理的意外行为?

时间:2015-01-17 22:47:36

标签: linux windows unix signals aix

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

不久前,我们要求将一些代码从AIX移到Linux上。看起来Linux有点不同,并没有花太长时间。通常,当抛出信号时,我们处理它并抛出C ++异常。那没有按预期工作。

Long story short, throwing c++ exceptions from a signal handler isn't going to work.

稍后,我整理了一个修复程序,它使用setjmp / longjmp将异常移出处理程序。在所有平台上都可以进行后续测试。经过一轮强制性的立体舞蹈之后,我开始进行一些单元测试。糟糕。

我的一些测试在Linux上失败了。我观察到的是,加强功能只能工作一次。使用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 **********

所以,我真正的问题是&#34; 这里发生了什么?&#34;

我的修辞问题是:

  • 还有其他人观察到这种行为吗?
  • 我应该怎么做才能解决这个问题?
  • 我应该注意哪些其他事情?

1 个答案:

答案 0 :(得分:3)

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

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