10.4 不可靠信号
在早期版本的UNIX系统中(比如说版本7),信号是不可靠的,意思是说信号可能丢失:一个信号出现,但是进程可能永远也不知道它,Aslo,a process had little conrol over a signal: a process could catch the signal or ignore it, 有时候,我们希望告知内核阻塞一个信号:不要忽略它,当信号出现的时候记住,然后在进程准备好以后通知进程。
在4.2BSD中做了修改,从而提供了一种称为reliable signals的东西,在SVR3上也做了另外一套修改,在System V上提供了可靠信号,POSIX.1最后选择了BSD模型来进行标准化。
使用早期版本信号处理的一个问题是:在每一次信号出现以后,信号的处置方式就会被恢复到其默认处理方式(在前一个例子中,我们仅仅运行每个信号一次,所以避免了这个问题。),描述这些早期系统的一个典型例子关注了如何处理中断信号。其代码描述看起来像如下所述:
int sig_int(); /*my signal handling function*/
...
.signal(SIGINT, sig_int); /*establish handler*/
...
sig_int()
{
signal(SIGINT, sig_int); /*establish handler for next time*/
... /*process the signal*/
}
signal 处理函数被声明为返回一个整形值的原因是:早期系统并不支持ISO C的void数据类型.
上述代码片段的问题是:在信号已经生成一次之后,但是在调用signal handler里面的signal函数之前,就会存在一个时间窗,在这个时间窗内,如果再出现一次中断信号,那么新出现的信号就会造成一次默认处理,导致进程被终止,虽然在许多时候进程工作看起来是很正常的,但是这只是一个假象。
这个早期版本的系统的另一个问题是:进程不能够关闭信号,即是进程不想改信号出现,所有进程可以实现的是忽略信号。有些时候我们想要告知系统:防止如下信号终止当前进程,但是如果他们出现的话请记住。阐述这一流程的典型事例如下所示:
int sig_int(); /*my signal handling function*/
int sig_int_flag; /*set nonzero when signal occurs*/
main()
{
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来将自身放到sleep状态中去,直到捕获到信号,但信号出现的时候,信号处理函数会设置标志sig_int_flag位一个非零值。在信号处理函数返回之后,进程被内核自动唤醒,进程检查到标志值非零,于是开启执行其计划的事情,但是存在一个时间窗可能使得事情变得非常糟糕,如果信号出现在检查sig_int_flag之后但是有恰巧在pause之前,进程就可能永远不能唤醒了(假设信号从此不再产生),那么该信号的出现就丢失了,这是一些代码出现错误的例子,虽然在很多时间看起来是正确的,但是这只是一个假象,调试这种类型的错误是非常困难的。