如何将 SIGFPE 与信号一起使用?
How to use SIGFPE with signal?
我刚刚在 C/C++ 中了解到 "signals" 并四处游玩。但是我在理解 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;
}
正如我所说,大多数信号都是 OS 特定的。对于 Linux,请仔细阅读信号处理程序中的 signal(7). You forgot a \n
inside your printf
(usually, you'll be lucky enough to see something work in your code, but read all my answer). And in principle you should not call printf
(which is not an async-signal-safe function, you should use directly and only write(2)。
可能发生的事情是(忽略信号处理程序中错误使用 printf
所造成的 undefined behavior)是:
您的 stdout
缓冲区永远不会被刷新,因为您在 printf
中忘记了 \n
(您可以添加 fflush(NULL);
...)在你的代码 my_handler
里面
可能,SIGFPE 处理程序再次重新启动,machine code instruction triggering it. (More exactly, after returning from sigreturn(2) 您的机器处于与交付 SIGFPE
之前相同的状态,因此相同的除以零条件发生等...)
处理 SIGFPE
很难(但如果您接受针对特定硬件和操作系统的代码进行编码,则可能会很痛苦);你会在你的信号处理程序中使用 sigaction(2) with SA_SIGINFO
and handle the third argument to the signal handler (which is a ucontext_t
pointer indirectly giving the machine state, including processor registers, which you might change inside your handler; in particular you could change your return program counter there). You might also consider using sigsetjmp(3) (但它在理论上是被禁止的,因为它不是异步信号安全的)。
(你当然需要了解处理器的细节instruction set architecture and your operating system's ABI;掌握这些之后你可能需要一个星期的编码工作)
以可移植的 POSIX 方式,SIGFPE
无法真正处理,如
中所述
可能 JVM or of SBCL is handling SIGFPE
in a machine & operating system specific way to report zero-divides as divide-by-zero exceptions .... (to Java programs for JVM, to Common Lisp programs for SBCL). Alternatively their JIT 的运行时或编译器机器可以在每个除法之前生成一个测试。
顺便说一句,信号处理程序中设置的标志应该声明 volatile sig_atomic_t
。参见 POSIX 规范关于 <signal.h>
作为实用的经验法则,POSIX 便携式和强大的信号处理程序应该只设置一些 volatile sig_atomic_t
and/or 或 write(2) a few bytes to some pipe(7) (your process could set up a pipe to itself -as recommended by Qt-, with another thread and/or some event loop reading it), but this does not work for asynchronous process 生成的信号,例如 [ =19=、SIGBUS
、SIGILL
和 SIGSEGV
等...(只能通过痛苦的计算机特定代码来处理)。
另见 this answer 一个非常相关的问题。
最后,在Linux,信号处理被认为不是很快。即使有很多特定于机器的编码,通过棘手的 SIGSEGV
处理来模拟 GNU Hurd external pagers(这会 mmap
懒惰......)被认为是相当慢的。
除以零是 undefined behaviour。因此,当您的程序调用未定义的行为时,是否为 SIGFPE
安装了处理程序意义不大。
POSIX 说:
Delivery of the signal shall have no effect on the process. The
behavior of a process is undefined after it ignores a SIGFPE, SIGILL,
SIGSEGV, or SIGBUS signal that was not generated by kill(),
sigqueue(), or raise().
信号作为事件的结果(例如,通过按 CTRL+C 发送 SIGINT
)可以由进程处理 if 表示事件非致命。 SIGFPE
是程序中的错误条件,您无法 处理 。类似的情况是尝试处理 SIGSEGV
,这等同于此(未定义的行为)。当您的进程试图访问一些它无权访问的内存时。如果你能无视它,当什么都没发生过,那就太傻了。
我刚刚在 C/C++ 中了解到 "signals" 并四处游玩。但是我在理解 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;
}
正如我所说,大多数信号都是 OS 特定的。对于 Linux,请仔细阅读信号处理程序中的 signal(7). You forgot a \n
inside your printf
(usually, you'll be lucky enough to see something work in your code, but read all my answer). And in principle you should not call printf
(which is not an async-signal-safe function, you should use directly and only write(2)。
可能发生的事情是(忽略信号处理程序中错误使用 printf
所造成的 undefined behavior)是:
您的
stdout
缓冲区永远不会被刷新,因为您在printf
中忘记了\n
(您可以添加fflush(NULL);
...)在你的代码my_handler
里面可能,SIGFPE 处理程序再次重新启动,machine code instruction triggering it. (More exactly, after returning from sigreturn(2) 您的机器处于与交付
SIGFPE
之前相同的状态,因此相同的除以零条件发生等...)
处理 SIGFPE
很难(但如果您接受针对特定硬件和操作系统的代码进行编码,则可能会很痛苦);你会在你的信号处理程序中使用 sigaction(2) with SA_SIGINFO
and handle the third argument to the signal handler (which is a ucontext_t
pointer indirectly giving the machine state, including processor registers, which you might change inside your handler; in particular you could change your return program counter there). You might also consider using sigsetjmp(3) (但它在理论上是被禁止的,因为它不是异步信号安全的)。
(你当然需要了解处理器的细节instruction set architecture and your operating system's ABI;掌握这些之后你可能需要一个星期的编码工作)
以可移植的 POSIX 方式,SIGFPE
无法真正处理,如
可能 JVM or of SBCL is handling SIGFPE
in a machine & operating system specific way to report zero-divides as divide-by-zero exceptions .... (to Java programs for JVM, to Common Lisp programs for SBCL). Alternatively their JIT 的运行时或编译器机器可以在每个除法之前生成一个测试。
顺便说一句,信号处理程序中设置的标志应该声明 volatile sig_atomic_t
。参见 POSIX 规范关于 <signal.h>
作为实用的经验法则,POSIX 便携式和强大的信号处理程序应该只设置一些 volatile sig_atomic_t
and/or 或 write(2) a few bytes to some pipe(7) (your process could set up a pipe to itself -as recommended by Qt-, with another thread and/or some event loop reading it), but this does not work for asynchronous process 生成的信号,例如 [ =19=、SIGBUS
、SIGILL
和 SIGSEGV
等...(只能通过痛苦的计算机特定代码来处理)。
另见 this answer 一个非常相关的问题。
最后,在Linux,信号处理被认为不是很快。即使有很多特定于机器的编码,通过棘手的 SIGSEGV
处理来模拟 GNU Hurd external pagers(这会 mmap
懒惰......)被认为是相当慢的。
除以零是 undefined behaviour。因此,当您的程序调用未定义的行为时,是否为 SIGFPE
安装了处理程序意义不大。
POSIX 说:
Delivery of the signal shall have no effect on the process. The behavior of a process is undefined after it ignores a SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal that was not generated by kill(), sigqueue(), or raise().
信号作为事件的结果(例如,通过按 CTRL+C 发送 SIGINT
)可以由进程处理 if 表示事件非致命。 SIGFPE
是程序中的错误条件,您无法 处理 。类似的情况是尝试处理 SIGSEGV
,这等同于此(未定义的行为)。当您的进程试图访问一些它无权访问的内存时。如果你能无视它,当什么都没发生过,那就太傻了。