我只是在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,但是它永远运行了,我不得不中断它。
signal == 11
这实际上使我的大脑冻结了几分钟,而signal()在今天和这个年龄段中永远不使用的原因在我体内变得越来越强大。
signal()
首先,从手册页 signal()
signal()的行为在UNIX版本之间有所不同,并且在历史上在Linux的不同版本中也有所不同。避免使用它:改为使用sigaction(2)。
再往下走:
* 如果将处置设置为函数,则首先将处置重置为SIG_DFL,或者阻止信号(请参见下面的可移植性),然后使用参数signum调用处理程序。如果调用处理程序导致信号被阻塞,则从处理程序返回后,信号将被解除阻塞。
在原来的Unix系统中,被安装的处理程序时,配置为重置为SIG_DFL,丝毫 未 阻断的输入信号 相同的 类型,然后将其跑处理函数。System V提供了此功能,而Linux内核也是如此。
这意味着,一旦代码在Linux系统上运行,一旦调用了第二个异常,它将直接退出。
现在到有趣的部分。BSD试图改善这种行为。再次从手册页:
在BSD上,当调用信号处理程序时,不会重置信号处理,并且在执行处理程序时会阻止传递其他信号实例。
并且由于mac osx部分基于BSD,因此一旦代码在mac osx上运行,一旦调用了第二个异常,它将 挂起 并等待第一个异常的处理程序退出。但是由于您将永远不会退出,因此您将陷入僵局。
那就是为什么一个人应该使用它sigaction()而不应该使用它signal()。
sigaction()
现在来一些提示:
处理程序应简短,并迅速返回。如果执行计算并调用其他函数,则可能是做错了什么。信号不能替代事件驱动的框架。
调用非异步安全的函数是不好的。考虑如果在调用期间发生异常fprintf,然后fprintf再次调用处理程序内部会发生什么情况。由于信号处理程序和程序数据都对流本身进行操作,因此它们都可能会损坏。
fprintf
更多阅读内容:信号处理程序中的“执行”和“请勿执行”