我只是告诉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;
}
答案 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 answer或JVM的运行时正在处理机器中的SIGFPE
。报告零除的操作系统特定方式为除零异常....(对于JVM的Java程序,对于SBCL的Common Lisp程序)。或者,他们的SBCL或编译器机器可以在每次除法之前生成测试。
BTW,应该在信号处理程序中设置一个标志SIGFPE
。请参阅有关JIT
作为一个实用的经验法则,POSIX可移植且强大的信号处理程序应该只为某些<signal.h>
设置一些volatile sig_atomic_t
和/或write(2)几个字节(您的进程可以设置)管道自身 - pipe(7) - ,另一个线程和/或某个事件循环读取它,但这不适用于异步 recommended by Qt - 生成的信号如volatile sig_atomic_t
,SIGFPE
,SIGBUS
和SIGILL
等等(只能由痛苦的计算机专用代码处理)。
另请参阅process一个非常相关的问题。
最后,在Linux上,信号处理被认为不是很快。即使有很多特定于机器的编码,通过棘手的SIGSEGV
处理(SIGSEGV
懒惰......)来模拟this answer被认为是非常慢的。
答案 1 :(得分:2)
除以零是undefined behaviour。因此,当程序调用未定义的行为时,是否为SIGFPE
安装了处理程序并不重要。
POSIX说:
信号的传递不应对过程产生影响。该 在忽略SIGFPE,SIGILL之后,进程的行为是不确定的, 不是由kill()生成的SIGSEGV或SIGBUS信号, sigqueue()或raise()。
由于某个事件引发了一个信号(例如,通过按 CTRL + C 发送SIGINT
,如果表示该过程可以由进程处理事件不致命。 SIGFPE
是程序中的错误条件,您无法处理。类似的情况是尝试处理SIGSEGV
,这相当于此(未定义的行为)。当您的进程尝试访问某些无法访问的内存时。如果你可以忽略它并继续进行,就好像什么也没发生一样,这将是愚蠢的。