繁体   English   中英

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

[英]How to use SIGFPE with signal?

我只是告诉自己有关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 (这不是异步信号安全函数,您应该直接使用它,而只能在内部使用write(2) )。

可能发生的是(忽略由于错误使用信号处理程序中的printf造成的未定义行为 ):

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

  • 可能是SIGFPE处理程序再次重新启动了触发它的机器代码指令。 (更确切地说,从sigreturn(2)返回之后,您的计算机与交付SIGFPE之前的状态相同,因此发生了相同的被零除的情况,等等。)

处理SIGFPE很难(但很痛苦,如果您接受对硬件和操作系统特定的代码的编码); 您可以将sigaction(2)SA_SIGINFO一起SA_SIGINFO并处理信号处理程序的第三个参数(这是ucontext_t指针,它间接提供机器状态,包括处理器寄存器 ,您可以在处理程序内部进行更改;特别是可以更改返回程序柜台那里)。 您可能还会考虑在信号处理程序中使用sigsetjmp(3) (但是从理论上讲,这是禁止的,因为它不是异步信号安全的)。

(您当然需要了解处理器的指令集体系结构和操作系统的ABI的详细信息 ;掌握这些知识后,可能需要一周的编码工作)

Blue Moon的答案所述 ,以便携式POSIX方式无法真正处理SIGFPE

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

顺便说一句,在信号处理程序中设置的标志应声明为volatile sig_atomic_t 有关<signal.h>请参见POSIX规范

根据经验,务实规则,POSIX便携和强大的信号处理程序应只设置一些volatile sig_atomic_t和/或可能写(2)几个字节到一些管道(7), (你的进程可以建立一个管道本身-如建议由Qt-通过另一个线程和/或某个事件循环读取),但这不适用于异步 过程生成的信号,例如SIGFPESIGBUSSIGILLSIGSEGV等(只能通过痛苦的方式来处理)计算机专用代码)。

另请参阅此答案是一个非常相关的问题。

最后,在Linux上,信号处理被认为不是很快。 即使有很多具体的机器编码,模拟GNU赫德外部寻呼机通过棘手的SIGSEGV处理(这将mmap懒洋洋....)被认为是相当缓慢。

除以零是未定义的行为 因此,当程序调用未定义的行为时,是否为SIGFPE安装处理程序就没有什么意义。

POSIX说:

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

由于事件(例如,通过按CTRL + C发送SIGINT )而引发SIGINT如果该事件不致命,则可由该进程处理。 SIGFPE是程序中的错误情况,您无法处理 类似的情况是尝试处理SIGSEGV ,这等效于此(未定义的行为)。 当您的进程尝试访问一些它没有访问权限的内存时。 如果您只是忽略它并继续进行,好像什么都没有发生,那将是愚蠢的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM