如何将SIGFPE与信号一起使用

How to use SIGFPE with signal?

本文关键字:一起 信号 SIGFPE      更新时间:2023-10-16

我只是告诉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;
}

正如我评论的那样,大多数信号都是特定于操作系统的。对于 Linux,请仔细阅读 signal(7(。你忘记了printf里面的n(通常,你会很幸运地看到你的代码中有一些东西在工作,但请阅读我所有的答案(。原则上你不应该从你的信号处理程序调用printf(这不是一个异步信号安全的函数,你应该直接使用,并且只在里面写(2(。

可能正在发生的事情是(忽略在信号处理程序中错误使用printf所带来的未定义行为(是:

  • 您的stdout缓冲区永远不会刷新,因为您忘记了代码内部my_handler printf中的n(您可能会添加fflush(NULL);...(

  • SIGFPE 处理程序可能会再次重新启动触发它的机器代码指令。(更确切地说,从 sigreturn(2( 返回后,您的机器处于与交付SIGFPE之前相同的状态,因此会发生相同的除以零条件,等等...

处理SIGFPE是很困难的(但非常痛苦,如果你接受编码特定于硬件和操作系统的代码(;你可以将 sigaction(2( 与 SA_SIGINFO 一起使用,并将第三个参数处理到信号处理程序(这是一个间接提供机器状态的ucontext_t指针,包括处理器寄存器,你可以在处理程序中更改它;特别是你可以在那里更改你的返回程序计数器(。你也可以考虑在信号处理程序中使用 sigsetjmp(3( (但理论上这是被禁止的,因为不是异步信号安全的(。

(你当然需要了解处理器的指令集架构和操作系统的ABI的细节;在掌握这些之后,你可能需要一周的编码工作(

以便携式POSIX的方式,SIGFPE无法真正处理,正如Blue Moon的回答中所解释的那样

可能,JVM或SBCL的运行时正在以机器和操作系统特定的方式处理SIGFPE,以将零除报告为除以零异常。(JVM的Java程序,SBCL的Common Lisp程序(。或者,他们的 JIT 或编译器机制可以在每个部门之前生成一个测试。

顺便说一句,信号处理程序内设置的标志应该声明为volatile sig_atomic_t。查看有关<signal.h>的 POSIX 规范

作为一个实用的经验法则,POSIX 可移植且健壮的信号处理程序应该只设置一些volatile sig_atomic_t和/或可能将(2(几个字节写入某个管道(7((您的进程可以为自己设置一个管道 - 正如Qt所推荐的那样-,另一个线程和/或某个事件循环读取它(,但这不适用于异步进程生成的信号,如SIGFPESIGBUSSIGILLSIGSEGV等...(这只能通过痛苦的计算机特定代码来处理(。

另请参阅对一个非常相关的问题的回答。

最后,在Linux上,信号处理被认为不是很快。即使有很多特定于机器的编码,通过棘手的SIGSEGV处理来模拟GNU Hurd外部寻呼机(这会懒惰地mmap......(被认为是相当慢的。

除以

零是未定义的行为。因此,当您的程序调用未定义的行为时,您是否安装了用于SIGFPE的处理程序意义不大。

波西克斯 说:

信号的传递对过程没有影响。这 进程的行为在忽略 SIGFPE、SIGILL 、 SIGSEGV,或不是由kill((生成的SIGBUS信号, sigqueue((,或 raise((。

信号

因事件(例如,通过按 CTRL+C 发送SIGINT(而引发,如果所述事件非致命,则进程可以处理该信号。 SIGFPE是程序中的错误条件,您无法处理。类似的情况是尝试处理SIGSEGV,这等同于这个(未定义的行为(。当您的进程尝试访问某些它无权访问的内存时。如果你可以忽略它并继续前进,就好像什么都没发生一样,那将是愚蠢的。