信号处理

我只是在Mac OS X中玩信号

为什么在我的信号处理程序完成之后,下面的代码不会产生SIGSEGV的默认行为? 在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; } 

编辑:我得到的输出是以下内容:

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

问题是我希望程序在输出signal == 11之后终止,但是它会永远传播,我必须中断它。

Solutions Collecting From Web of "信号处理"

这实际上让我脑冻了几分钟,为什么一个人不应该在这个时代使用signal() ,只能在我身上变得更强。

首先,从signal()的手册页

signal()的行为在UNIX版本中有所不同,并且在不同版本的Linux中历史上也有所不同。 避免使用:改用sigaction(2)。

并进一步下降:

  • 如果配置被设置为一个函数,那么首先将配置重置为SIG_DFL,或者信号被阻塞(见下面的可移植性),然后使用参数signum调用处理程序。 如果处理程序的调用导致信号被阻塞,则从处理程序返回时信号将被解除阻塞。

在原来的Unix系统中,当安装一个处理程序时,处置被重置为SIG_DFL, 不会阻塞相同类型的传入信号,然后运行处理函数。 System V提供了这个,而且linux内核也是这样做的。

这意味着,一旦代码在Linux系统上运行,一旦调用了第二个异常,它将直接退出。

现在到有趣的部分。 BSD试图改善这种行为。 再次从man页面:

在BSD上,当一个信号处理程序被调用时,信号处置没有被重置,并且在处理程序正在执行时信号的其他实例被阻止被传递。

而且由于mac osx部分基于BSD,一旦代码在mac osx上运行,一旦调用了第二个异常,它将等待处理,并等待第一个异常的处理程序退出。 但是既然你永远不会退出,你就会陷入僵局。

这就是为什么一个人应该使用sigaction()而不是signal()

现在来看一些提示:

经办人员应该是短暂的,并快速返回。 如果你正在执行计算和调用其他函数,你可能做错了什么。 信号不能替代事件驱动的框架。

调用不是异步安全的函数是不好的。 考虑一下如果在调用fprintf过程中发生异常会发生什么情况,再次调用fprintf处理函数。 信号处理程序和程序数据都可能被破坏,因为它们在流本身上运行。

更多阅读: 信号处理程序中的“做”和“不要”

根据POSIX :

如果SIGFPE,SIGILL,SIGSEGV或SIGBUS信号在被阻塞时产生,结果是未定义的,除非信号由kill()函数, sigqueue()函数或raise()函数生成。

因为SIGSEGV在SIGSEGV信号处理程序中被阻塞,所以在这种情况下结果是不确定的,任何行为都是有效的。 如果你不希望它被阻塞,你可以使用带有SA_NODEFER标志的sigaction()来安装信号处理程序,或者使用sigprocmask()去解除信号处理程序中的信号。

在调试器中运行它,并单步执行您希望崩溃的指令。

不能保证写入无效地址必须产生分段错误。 也许Mac OS X为你映射这些地址,而你正在覆盖良性的东西。