分类: LINUX
2012-01-22 20:22:03
++++++APUE读书笔记-10信号-04不可靠的信号++++++
4、不可靠的信号
================================================
在早期的unix版本中,信号是不可靠的,也就是说,信号是容易丢失的(信号发生了,但是进程却不知道信号已经发生了);同时,进程对信号的控制也是有限的,进程只能获取或者忽略一个信号. 有时候我们需要告诉内核来阻塞一个信号,也就是说,产生信号的时候不忽略这个信号而是记住这个信号,当我们准备好了之后再告诉我们。
在4.2BSD的时候,做了一些改变提供了可靠的信号机制,在SVR3的时候,有一套不同的改变的机制为SystemV实现可靠的信号机制,POSIX.1把BSD的模式作为了标准模式。
早期的问题是,每当信号发生的时候,信号对应的动作会被重置为它的默认值.(在前面的例子中,当我们运行程序的时候,我们忽略了这个问题,只对信号捕捉一次),一般来说经典书籍里面早期系统中处理信号中断的代码大致如下:
int sig_int(); /* 自定义的信号处理函数 */
...
signal(SIGINT, sig_int); /* 建立信号处理函数和信号之间的联系 */
...
sig_int()
{
signal(SIGINT, sig_int); /* 再次建立处理函数和信号之间的联系 */
... /* 处理信号 ... */
}
(这里,信号处理函数返回int类型的原因是早期的系统不支持ISO C的void类型.)
前面的代码片段的一个问题是,有一个时间窗口,它发生在信号产生的时候之后,信号处理函数时候调用signal再次建立信号处理函数连接之前;期间可能会再次发生一次信号。这第二次发生信号的时候,信号处理的函数已经被重置为默认的了,还没有来的及设置就被执行了(导致进程终止)。大多数时候,代码都能正常的工作,但是如果有这样的情况,我们就需要仔细考虑考虑了。
另外一个问题就是,早期的系统进程,当它不想信号发生的时候,不能将一个信号“关闭”。所有进程只能忽略这个信号,有时后,我们想要告诉系统“阻止这些信号发生,但是当它们发生的时候把它们记住”。描述这个缺陷的典型的例子(捕获这个信号,然后为进程设置一个标记表明这个信号发生过):
int sig_int_flag; /* 当信号发生的时候将这里设置为非0 */
main()
{
int sig_int(); /* 自定义的信号处理函数 */
...
signal(SIGINT, sig_int); /* 建立信号和处理函数之间的联系 */
...
while (sig_int_flag == 0)
pause(); /* 暂停,等待信号发生就进入下一次循环 */
...
}
sig_int()
{
signal(SIGINT, sig_int); /* 再次建立处理函数和信号之间的联系 */
sig_int_flag = 1; /* 设置main函数中的循环检测的标记 */
}
这里的例子,进程调用pause函数进入睡眠等待一直到捕捉了一个信号。当信号被捕捉的时候,信号处理函数会设置sig_int_flag为非0。这个进程在信号处理函数返回的时候,自动地被内核唤醒,然后会注意到标记变成了非0,然后做相应的处理。但是,存在一个可以导致问题的时间窗口:如果信号发生在检测sig_int_flag之后但是调用pause之前,那么进程就可能永远睡眠不会醒过来了(前提假设是信号不会再次发生了).这样,信号就丢失了,这就是另外一个问题。尽管这个问题发生的可能性很小,但是如果它发生了,那么就很难调试。
参考: