Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1798327
  • 博文数量: 438
  • 博客积分: 9799
  • 博客等级: 中将
  • 技术积分: 6092
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-25 17:25
文章分类

全部博文(438)

文章存档

2019年(1)

2013年(8)

2012年(429)

分类: 系统运维

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,来重新建立这个处理器。(这个动作是使时间间隙最小化,当信号被重置为它的默认值并可能丢失时。)我们展示下面的代码:



  1. #include <sys/wait.h>

  2. static void sig_cld(int);

  3. int
  4. main()
  5. {
  6.     pid_t pid;

  7.     if (signal(SIGCLD, sig_cld) == SIG_ERR)
  8.         perror("signal error");
  9.     if ((pid = fork()) < 0)
  10.         perror("fork error");
  11.     else if (pid == 0) { /* child */
  12.         sleep(2);
  13.         _exit(0);
  14.     }
  15.     pause(); /* parent */
  16.     exit(0);
  17. }

  18. static void
  19. sig_cld(int signo) /* interrupts pause() */
  20. {
  21.     pid_t pid;
  22.     int status;

  23.     printf("SIGCLD received\n");
  24.     if (signal(SIGCLD, sig_cld) == SIG_ERR) /* reestablish handler */
  25.         perror("signal error");
  26.     if ((pid = wait(&status)) < 0) /* fetch child status */
  27.         perror("wait error");
  28.     printf("pid = %d\n", pid);
  29. }

这个程序在一些平台上不能工作。如果我们在一个传统的系统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。

阅读(1636) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~