简体   繁体   English

信号处理

[英]signal handling

I'm just playing with signal in Mac OS X. 我只是在Mac OS X中玩信号。

Why does the following code not produce the default behavior of SIGSEGV after my signal handler has finished? 为什么在我的信号处理程序完成后,以下代码不会产生SIGSEGV的默认行为? Under Linux, the code works fine. 在Linux下,代码工作正常。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void err_crash();

void my_signal_handler (int signal)
{
    fprintf(stderr, "signal == %i\n", signal);
    fflush(stderr);
    err_crash();
}

void err_crash()
{
    static int count= 0;
    if (count)
        signal(SIGSEGV, SIG_DFL);       /* break recursion loop if we recursed */
    count++;

    // one of the two should really crash ;)
    ((void)(*((int volatile *)NULL)));
    *((int *)NULL) = 42;

    abort();                            /* in case we're not crashed yet... */
}

int main ()
{
    signal(SIGSEGV, my_signal_handler);
    err_crash();
    return 0;
}

EDIT: The output I get is the following: 编辑:我得到的输出如下:

bonecrusher:devel sw$ g++ signal_problems.cpp -o foo
bonecrusher:devel sw$ ./foo 
signal == 11
^C
bonecrusher:devel sw$

The problem is that I want that the program terminates after the output of signal == 11 , but it rans forever and I have to interrupt it. 问题是我希望程序在signal == 11的输出之后终止,但它永远发生,我必须打断它。

This actually caused me brain freeze for a few minutes, and the reason why one should never use signal() in this day and age only grew stronger in me. 这实际上让我大脑冻结了几分钟,而且在这个时代,人们不应该使用signal()的原因只会在我身上变得更强。

First of all, from the man pages for signal() 首先,从signal()的手册页

The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. signal()的行为在UNIX版本中各不相同,并且在不同版本的Linux上也有不同的历史变化。 Avoid its use: use sigaction(2) instead. 避免使用:改为使用sigaction(2)。

and further down : 进一步向下:

  • If the disposition is set to a function, then first either the disposition is reset to SIG_DFL, or the signal is blocked (see Portability below), and then handler is called with argument signum. 如果处置设置为函数,则首先将处置重置为SIG_DFL,或者阻止信号(参见下面的可移植性),然后使用参数signum调用处理程序。 If invocation of the handler caused the signal to be blocked, then the signal is unblocked upon return from the handler. 如果处理程序的调用导致信号被阻塞,则在从处理程序返回时信号被解除阻塞。

In the original Unix systems, when a handler was installed, the disposition was reset to SIG_DFL, did not block incoming signals of the same type, and then it ran the handler function. 在原来的Unix系统中,被安装的处理程序时,配置被复位为SIG_DFL, 阻断的相同类型的输入信号,然后将其跑处理函数。 System V provided this, and the linux kernel does the same. System V提供了这个,而linux内核也是如此。

This means that, once the code is run on a linux system, once second exception is called, it will exit directly. 这意味着,一旦代码在linux系统上运行,一旦调用第二个异常,它将直接退出。

Now to the fun part. 现在到了有趣的部分。 BSD tried to improve this behaviour. BSD试图改善这种行为。 From the man pages again: 再次从手册页:

On BSD, when a signal handler is invoked, the signal disposition is not reset, and further instances of the signal are blocked from being delivered while the handler is executing. 在BSD上,当调用信号处理程序时,信号处理不会被重置,并且在处理程序执行时阻止信号的其他实例被传递。

And since mac osx is partly based on BSD, once the code is run on a mac osx, once second exception is called, it will be pending and wait for the handler of the first exception to exit. 由于mac osx部分基于BSD,一旦代码在mac osx上运行,一旦调用第二个异常,它将处于挂起状态并等待第一个异常的处理程序退出。 But since you will never exit, you have a deadlock. 但既然你永远不会退出,那你就陷入了僵局。

Thats why one should use sigaction() instead and never signal() . 这就是为什么一个人应该使用sigaction()而不是signal()

Now to some tips: 现在来一些提示:

Handlers should be short, and return quickly. 处理程序应该简短,并快速返回。 If you are performing calculations and calling other functions you are probably doing something wrong. 如果您正在执行计算并调用其他函数,那么您可能做错了。 Signals are not a substitute for an event driven framework. 信号不能替代事件驱动的框架。

Calling functions that are not async-safe is bad. 调用非异步安全的函数很糟糕。 Consider what would happen if an exception happened during a call to fprintf , and inside the handler fprintf was called again. 考虑如果在调用fprintf期间发生异常会发生什么,并且在处理程序内部再次调用fprintf Both the signal handlers and the programs data could be corrupted since they operate on the stream itself. 信号处理程序和程序数据都可能被破坏,因为它们在流本身上运行。

Some more reading : "Do" and "Don't" inside A Signal Handler 更多阅读: “信号处理程序”中的“Do”和“Do not”

According to POSIX : 根据POSIX

If any of the SIGFPE, SIGILL, SIGSEGV, or SIGBUS signals are generated while they are blocked the result is undefined, unless the signal was generated by the kill() function, the sigqueue() function, or the raise() function. 如果在阻塞时生成任何SIGFPE,SIGILL,SIGSEGV或SIGBUS信号,则结果是未定义的,除非信号由kill()函数, sigqueue()函数或raise()函数生成。

Because SIGSEGV is blocked while in the SIGSEGV signal handler, the result is undefined in this case and any behavior is valid. 因为SIGSEGV在SIGSEGV信号处理程序中被阻塞,所以在这种情况下结果是未定义的,并且任何行为都是有效的。 If you don't want it to be blocked you could install the signal handler using sigaction() with the SA_NODEFER flag, or use sigprocmask() to unblock the signal within the signal handler. 如果您不希望它被阻止,您可以使用带有SA_NODEFER标志的sigaction()安装信号处理程序,或使用sigprocmask()来取消阻塞信号处理程序中的信号。

Run it in a debugger and single-step the instructions that you expect to crash. 在调试器中运行它并单步执行您希望崩溃的指令。

There is no guarantee that writing to invalid addresses must create a segmentation fault. 无法保证写入无效地址必须创建分段错误。 Perhaps Mac OS X maps those addresses for you, and you're overwriting something benign. 也许Mac OS X会为您映射这些地址,而您正在覆盖一些良性的东西。

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

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