不可靠的信号
在早期的UNIX系统版本中信号曾是不可靠的。它的意思是信号可能丢失:一个信号发生后进程可能永远不会知道它曾发生过。同样,进程也可以控制信号:进程可以捕获信号或忽略它。有时我们可能希望告诉内核阻塞一个信号:别忽略它,当信号发生时记住它,然后当我们准备好后再告诉我们。
早其版本的一个问题是每次信号发生时都会重置处理动作为它的默认动作。(在之前的例子Figure 10.2中,我们通过捕捉每种信号一次,来避免这种情况。)通常的处理代码如下:
int sig_int(); /* my signal handling function */
...
signal(SIGINT, sig_int); /* establish handler */
...
sig_int()
{
signal(SIGINT, sig_int); /* reestablish handler for next time */
... /* process the signal ... */
}
|
这段代码的问题是,当一个中断发在生另一个中断处理函数(signal handler)正在执行时有一个时间间隙(时间窗口window of time)。第二个中断可能会导致默认动作发生,它会结束这个信号的进程。这会让我们误认为这是正常行为,其实它不是。
早期版本的另一个问题是进程不能在它不想让信号发生时关闭信号。所有进程都可能忽略信号。某时我们可能想在信号发生时只是等待以后再处理。典型的处理方法是如果信号发生了对进程设置一个标记:
int sig_int_flag; /* set nonzero when signal 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到非零值。在信号处理函数反回后内核自动换醒进程,注意标记非零并且可以做它任何须要做的。但是在时间间隙中可能会出一些错误。如果信号发生在测试sig_int_flag值之后,但pause还没有调用之前,进程可以一直休眠下去(假设该信号不再发生第二次)。这就是信号丢失事件。这会使其它代码都出错,但它仍然可以在大多数情况下正常工作。所以解决这类问题非常困难!