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

标签 linux windows unix signals aix

我生活的环境有 Win7/MSVC 2010sp1,两个不同的 Linux 机器 (Red Hat) g++ 版本(4.4.7、4.1.2)和 AIX xlc++ (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 上失败了。我观察到的是 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。我鼓励您使用更可靠的 sigaction 接口(interface)。第一个好处是您不再需要在处理程序中重置信号捕获...<​​/p>

关于linux - 为什么我遇到 Linux 信号处理的意外行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28005187/

相关文章:

java - 安装 Apache Tomcat

c++ - 如何在 Windows 上修复 cmake find_package "Could NOT find SDL2"?

c++ - boost::32 和 64 位进程之间的进程间共享内存

linux - sed 模式匹配直到第一次匹配

ruby-on-rails - 我需要在所有目标机器(Capistrano)上安装公钥吗?

python - 如何从外部命令结合管道获取输出

linux - 如何在 shell 脚本中正确地进行字符串比较?

linux - grunt watch/sass 无法正常工作

linux - Windows shell 脚本中的 echo %ERRORLEVEL% 与 echo $?在Linux中: are there any differences in behavior?

macos - 通过进程名称使用 Bash 检查 Mac 进程是否正在运行