分类: 系统运维
2012-03-30 19:08:15
两个不停产生混淆的信号的SIGCLD和SIGCHLD。首先,SIGCLD(没有H)是系统V的名字,这个信号有着和名为SIGCHLD的BSD信号不同的语义。POSIX.1信号也被命名为SIGCHLD。
BSD的SIGCHLD的语义是普通的,因为它的语义和其它所有信号的相似。当信号发生时,一个子进程的状态已经改变了,而我们必须调用某个wait函数来决定发生了什么事。
然 而,系统V,传统地又与其它信号不同的方式来处理SIGCLD信号。基于SVR4的系统继续这个可置疑的传统(也说是说,兼容性的限制),如果我们用 signal或sigset(更老的,与SVR3兼容的设置一个信号的布署的函数)来设置它的布署。SIGCLD的更老的处理由下面组成:
1、如
果进程指定设置它的布署为SIG_IGN,那么调用进程的子进程将不会产生僵尸进程。注意它和默认动作(SIG_DFL)不同,而是忽略这个信号。相反,
在终止时,子进程的状态被舍弃。如果它接着调用某个wait函数,那么调用进程会阻塞,直到它所有的子进程终止,然后wait返回-1,errno被设为
ECHILD。(这个信号的默认布署是忽略,但是这个默认动作不会导致上述语义发生。相反,我们必须确切设置它的布署为SIG_IGN)。
POSIX.1不指定当SIGCHLD被忽略时会发生什么,所以这个行为被允许。SUS包含了一个XSI扩展,指定这个行为要被SIGCHLD支持。
4.4BSD总是产生僵尸,如果SIGCHLD被忽略。如果我们想要阻止僵尸,我们必须等待我们的子进程。FreeBSD 5.2.1和4.4BSD一样工作。然而,Mac OS X 10.3当SIGCHLD被忽略时不创建僵尸。
在SVR4上,如果signal或sigset被调用并设置SIGCHLD的布署为忽略,那么僵尸不会被产生。Solaris 9和Linux 2.4.22遵循SVR4的行为。
使用sigaction,我们可以设置SA_NOCLDWAIT标志来避免僵尸。这个动作被所有四个平台支持。
2、如果我们设置SIGCLD为被捕获,那么内核立即检查是否有任何子进程准备好被等待,如果有,调用SIGCLD处理器。
第 二项改变了我们必须为这个信号写一个信号处理器的方式,正如下面的例子证明的。回想下10.4节,一个处理器的入口里要做的第一件事是再次调用 signal,来重新建立这个处理器。(这个动作是使时间间隙最小化,当信号被重置为它的默认值并可能丢失时。)我们展示下面的代码:
这个程序在一些平台上不能工作。如果我们在一个传统的系统V平台上编译和运行它,比如OpenServer 5或UnixWare 7,那么输出是连续的SIGCLD received的字符行。最终,进程用尽栈空间并异常退出。
FreeBSD 5.2.1和Mac OS X 10.3没有这个问题,因为基于BSD的系统一般不支持历史上的系统V的SIGCLD的语义。Linux2.4.22也不会有这个问题,因为当一个进程布置好要捕获SIGCHLD而子进程准备好被等待了的时候,它不会调用SIGCHLD的信号处理器,即使SIGCLD和SIGCHLD被定义为相同的值。另 一方面,Solaris 9,在这个情况下确实会调用信号处理器,但是在内核包含额外的代码来避免这个问题。
尽管本文的四个平台都解决了这个问题,但是要知道仍然有平台(比如UnixWare)没有解决它。
这个程序的问题是在信号处理器的开头的signal调用导致了前面讨论的第二项--内核查看一个子进程是否需要被等待(因为我们正在处理一个SIGCLD信号,所以是有),所以它产生另一个信号处理器的调用。这个信号处理器调用signal,于是整个处理又重新开始。
为了解决这个问题,我们必须把signal的调用移到wait调用之后。通过这样做,我们在得到子进程的终止状态后调用signal,仅当其它子进程终止时内核会再次产生信号。
POSIX.1 指出当我们建立一个SIGCHLD的信号处理器而已经有一个我们还未等待的已终止的子进程时,信号是否被产生是没有规定的。这允许前面描述的行为。但是因 为当信号发生时,POSIX.1没有把一个信号的布署重置为默认值,(假定我们使用POSIX.1的sigaction函数来设置它的布署,)所以我们不 需要在这个处理器里建立一个信号处理器。
要知道在你的实现里SIGCHLD的语义。特别要知道一些定义SIGCHLD为SIGCLD或反过来的系统。改变这个名字可能允许你编译在另一个系统上之写的程序,但是如果程序依赖于其它语义,它可能不能工作。
本文讨论的四个平台上,SIGCLD等价于SIGCHLD。