如何使用SIGFPE与信号? [英] How to use SIGFPE with signal?

查看:210
本文介绍了如何使用SIGFPE与信号?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚通知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()
{

信号(SIGFPE,my_handler);
int result = 0;

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

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

getchar();

return 0;
}


解决方案

是操作系统特定的。对于Linux,请仔细阅读信号(7)。您忘记了 \\\
在您的 printf 里面(通常,您会幸运地看到您的代码,但是阅读我所有的答案)。原则上你不应该调用 printf (这不是一个异步信号安全的函数,你应该直接使用 write(2)里面)。



可能发生的情况是(忽略未定义的行为,错误地使用 printf 在信号处理程序中)是:




  • 您的 stdout (您可能会添加一个 fflush(NULL);

  • printf 里面 my_handler
  • 可能,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 或编译器机制可以在每个分区之前生成测试。



BTW,信号处理程序中设置的标志应该声明为 volatile sig_atomic_t 。有关 < signal.h>



作为一个实用的经验法则,POSIX便携和鲁棒的信号处理程序应该只设置一些 volatile sig_atomic_t 和/或可能写入(2)几个字节到某些 pipe(7)(您的过程可以设置一个自己的管道 - 由由Qt推荐 - ,另一个线程和/或一些事件循环读取它),但这不适用于异步 过程 - 生成的信号如 SIGFPE SIGBUS SIGILL SIGSEGV 等(只能通过痛苦的计算机专用代码处理)。



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



最后,在Linux上,信号处理被认为不是很快。即使有很多特定于机器的编码,也可以仿效 GNU Hurd外部寻呼机< a> by tricky SIGSEGV 处理(这将 mmap lazily ....)被认为是相当缓慢。 / p>

I just informed myselve about "signals" in C/C++ and played around. But i have a problem to understand the logic of SIGFPE.

I wrote a little program which will run into a division by zero, if this happens then the signal should be triggered and the signal handler should be executed. But instead my program just crashes. So what is the purpose of the SIGFPE if it does not even work on division by zero?

#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;
}

解决方案

As I commented, most signals are OS specific. For Linux, read carefully 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) inside) from your signal handler.

What probably is happening is (ignoring the undefined behavior posed by wrongly using printf inside the signal handler) is that:

  • your stdout buffer is never flushed since you forgot a \n (you might add a fflush(NULL);...) in the printf inside my_handler in your code

  • probably, the SIGFPE handler restarts again the machine code instruction triggering it. (More exactly, after returning from sigreturn(2) your machine is in the same state as it was before SIGFPE was delivered, so the same divide-by-zero condition happens, etc...)

It is difficult (but painfully possible, if you accept coding hardware-specific and operating-system specific code) to handle SIGFPE; you would use 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) inside your signal handler (but it is in theory forbidden, since not async-signal-safe).

(You certainly need to understand the details of your processor's instruction set architecture and your operating system's ABI; and you probably would need a week of coding work after having mastered these)

In a portable POSIX way, SIGFPE cannot really be handled, as explained in Blue Moon's answer

Probably, the runtime of 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 or compiler machinery could generate a test before every division.

BTW, a flag set inside a signal handler should be declared volatile sig_atomic_t. See POSIX specification about <signal.h>

As a pragmatical rule of thumb, a POSIX portable and robust signal handler should only set some volatile sig_atomic_t and/or perhaps 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-generated signals like SIGFPE, SIGBUS, SIGILL, and SIGSEGV, etc... (which could only be handled by painful computer-specific code).

See also this answer to a very related question.

At last, on Linux, signal processing is believed to be not very quick. Even with a lot of machine-specific coding, emulating GNU Hurd external pagers by tricky SIGSEGV handling (which would mmap lazily ....) is believed to be quite slow.

这篇关于如何使用SIGFPE与信号?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆