分类: 系统运维
2012-03-30 18:57:27
在UNIX系统的早期版本(比如版本7),信号是不可靠的。我们的意思是信号可能会丢失:一个信号可能发生而进程可能不知道。还有,一个进程不能很 好地控制信号:一个进程可以捕获或忽略它。有时,我们想告诉进程来阻塞一个信号:不要忽略它,如果它发生时只是记住,稍后在我们准备好时再告诉我们。
在4.2BSD时的修改提供了可靠信号。一堆不同的改变在SVR3上发生了,以提供在系统V下的可靠信号。POSIX.1选择BSD模式来标准化。
这些早期版本的一个问题是每次信号发生时,信号的动作被重置为它的默认值。(在上一节的例子,我们只捕获每个信号一次来避免这个细节。)编程书上的描述这些更早系统的经典例子关心如何处理中断信号。被描述的代码通常看起来像:
int sig_int(); /* my signal handling funciton */
...
signal(SIGINT, sig_int); /* establish handler */
...
sig_int()
{
signal(SIGINT, sig_int); /* reestablish handler for next time */
... /* process the signal ... */
}
信号处理器被声明为返回整型的原因是早期系统不支持ISO C的void数据类型。
这个代码片段的问题是有一个时间间隙--在信号发生后,但在信号处理器里的signal调用前--这时中断信号可能再次发生。第二个信号可能导致默认的动作发生,也就是终止这个进程。这是多数时间正确工作的一个情况,导致我们以为它是正确的,然而它不是。
这些早期系统的另一个问题是当它不想信号发生时,进程不能关闭信号。进程可以做的所有事是忽略这个信号。有时我们想告诉系统“阻止后续信号发生,但是记住它们的发生”。演示这个缺点的经典的例子,由捕获一个信号并为进程设置标志以指明信号发生过的一段代码展示:
int sig_int_flag; /* set nonzero when siganl occurs */
main()
{
int sig_int(); /* my signal handling function */
...
signal(SIGINT, sig_int); /* establish handler */
...
while (sig_int_flag == 0)
pause(); /* go to sleep, waiting for signal */
...
}
sig_int()
{
signal(SIGINT, sig_int); /* reestablish handler for next time */
sig_int_flag = 1; /* set flag for main loop to examine */
}
这 里,进程调用pause函数来使它睡眠,直到一个信号被捕获。当信号被捕获时,信号处理器只是把标志sig_int_flag设置为非0值。进程在信号处 理器返回时自动被内核唤醒,注意标志是非0值,并做任何它所需要做的事。但是有一个时间间隙,事情可能出错。如果信号在sig_int_flag测试之后 发生,但在pause调用之前,那么进程可能永远地睡过去了(假定这个信号不再产生)。这个信号的出现被丢失了。这是不对的代码多数时间工作的另一个例 子。调试这种类型的问题会比较困难。