如何将SIGFPE与信号一起使用?

时间:2015-11-07 16:53:36

标签: c++ signals

我只是告诉myselve关于C / C ++中的“信号”并且玩了。但我有一个问题需要理解SIGFPE的逻辑。

我写了一个小程序,它会在零处运行,如果发生这种情况,那么应该触发信号并执行信号处理程序。但相反,我的程序崩溃了。那么SIGFPE的目的是什么,如果它甚至不能除以零呢​​?

#include <stdio.h>
#include <signal.h>
#include <iostream>

int signal_status = 0;

void my_handler (int param)
{
    signal_status = 1;
    printf ("DIVISION BY ZERO!");
}

int main ()
{

    signal (SIGFPE, my_handler);
    int result = 0;

    while(1)
    {
        system("cls");
        printf ("signaled is %d.\n", signal_status);

        for(int i=10000; i>-1; i--)
        {
            result = 5000 / i;
        }
    }

    getchar();

    return 0;
}

2 个答案:

答案 0 :(得分:2)

正如我评论的那样,大多数信号都是特定于操作系统的。对于Linux,请仔细阅读signal(7)。您在\n内忘记了printf(通常情况下,您会很幸运地看到代码中的内容有效,但请阅读我的所有答案)。原则上你不应该从你的信号处理程序中调用printf(这不是异步信号安全函数,你应该直接使用,而只是write(2))。

可能发生的事情是(忽略在信号处理程序中错误地使用printf所造成的undefined behavior):

  • 您的stdout缓冲区永远不会被刷新,因为您在\n内{{1}忘记了fflush(NULL);(您可以添加printf ...)在你的代码中

  • 可能,SIGFPE处理程序再次重启machine code指令触发它。 (更确切地说,从sigreturn(2)返回后,您的机器处于与my_handler交付之前相同的状态,因此会发生相同的被零除的情况......等等。

很难(但是,如果您接受编码特定于硬件和操作系统的特定代码,则很痛苦)来处理SIGFPE;您将sigaction(2)SIGFPE一起使用并处理信号处理程序的第三个参数(它是一个SA_SIGINFO指针间接给出机器状态,包括{{3你可以在你的处理程序中更改;特别是你可以在那里更改你的返回processor registers。您也可以考虑在信号处理程序中使用program counter(但理论上它是禁止的,因为它不是async-signal-safe)。

(您当然需要了解处理器的sigsetjmp(3)架构和操作系统的instruction set的详细信息;并且在掌握了这些架构后,您可能需要一周的编码工作)

以便携式POSIX方式,无法真正处理ucontext_t,如ABI

中所述

可能是Blue Moon's answerJVM的运行时正在处理机器中的SIGFPE。报告零除的操作系统特定方式为除零异常....(对于JVM的Java程序,对于SBCL的Common Lisp程序)。或者,他们的SBCL或编译器机器可以在每次除法之前生成测试。

BTW,应该在信号处理程序中设置一个标志SIGFPE。请参阅有关JIT

的POSIX规范

作为一个实用的经验法则,POSIX可移植且强大的信号处理程序应该只为某些<signal.h>设置一些volatile sig_atomic_t和/或write(2)几个字节(您的进程可以设置)管道自身 - pipe(7) - ,另一个线程和/或某个事件循环读取它,但这不适用于异步 recommended by Qt - 生成的信号如volatile sig_atomic_tSIGFPESIGBUSSIGILL等等(只能由痛苦的计算机专用代码处理)。

另请参阅process一个非常相关的问题。

最后,在Linux上,信号处理被认为不是很快。即使有很多特定于机器的编码,通过棘手的SIGSEGV处理(SIGSEGV懒惰......)来模拟this answer被认为是非常慢的。

答案 1 :(得分:2)

除以零是undefined behaviour。因此,当程序调用未定义的行为时,是否为SIGFPE安装了处理程序并不重要。

POSIX说:

  

信号的传递不应对过程产生影响。该   在忽略SIGFPE,SIGILL之后,进程的行为是不确定的,   不是由kill()生成的SIGSEGV或SIGBUS信号,   sigqueue()或raise()。

由于某个事件引发了一个信号(例如,通过按 CTRL + C 发送SIGINT,如果表示该过程可以由进程处理事件不致命。 SIGFPE是程序中的错误条件,您无法处理。类似的情况是尝试处理SIGSEGV,这相当于此(未定义的行为)。当您的进程尝试访问某些无法访问的内存时。如果你可以忽略它并继续进行,就好像什么也没发生一样,这将是愚蠢的。