当前位置:嗨网首页>书籍在线阅读

13-忽略终止的子进程

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

26.3.3 忽略终止的子进程

更有可能像这样处理终止子进程:将对SIGCHLD的处置(disposition)显式置为SIG_ IGN,系统从而会将其后终止的子进程立即删除,毋庸转为僵尸进程。这时,会将子进程的状态弃之不问,故而所有后续的wait()(或类似)调用不会返回子进程的任何信息。

注意,虽然对信号SIGCHLD的默认处置就是将其忽略,但显式设置对SIG_IGN标志的处置还是会导致这里所描述的行为差异。在这方面,对信号SIGCHLD的处理非常独特,不同于其他信号。

如同许多 UNIX 实现一样,在 Linux 系统中将对 SIGCHLD 信号的处置置为 SIG_IGN 并不会影响任何既有僵尸进程的状态,对它们的等待仍然要照常进行。在其他一些 UNIX 实现中(例如Solaris 8),将对SIGCHLD的处置设置为SIG_IGN确实会删除所有已有的僵尸进程。

信号SIGCHLD的SIG_IGN语义由来已久,源于系统V(System V)。SUSv3也规定了此处所描述的行为,不过原始的 POSIX.1 标准对此则未作表述。因此,在一些较老的 UNIX 实现中,忽略SIGCHLD并不影响僵尸进程的创建。要防止产生僵尸进程,唯一完全可移植的方法就是(可能是从 SIGCHLD 信号处理程序的内部)调用 wait()或者waitpid()。

老版本Linux内核实现与SUSv3标准的差异

SUSv3规定,如果将对SIGCHLD的处置设置为SIG_IGN,那么将丢弃子进程的资源使用信息,且若指定RUSAGE_CHILDREN标志调用getrusage()函数,其返回总量中也将不包含该项信息(36.1节)。然而,在版本2.6.9之前的Linux内核中,还是会记录子进程的CPU使用时间以及资源的使用情况,并可通过getrusage()调用获取。这一违规行为直至Linux 2.6.9才得以修正。

将对SIGCHLD的处置设置为SIG_IGN还会阻止times()(10.7节)返回的结构中包含子进程的CPU使用时间。不过,在Linux 2.6.9之前,times()所返回的信息同样存在违规行为。

SUSv3规定,如果将对 SIGCHLD 的处置设置为 SIG_IGN,同时,父进程已终止的子进程中并无处于僵尸状态且未被等待的情况,那么wait()(或waitpid())调用将一直阻塞,直至所有子进程都终止,届时该调用将返回错误ECHILD。Linux 2.6符合这一要求。不过在Linux 2.4以及更早期的版本中,wait()只会阻塞到下一个子进程终止的时刻,随即返回该子进程的进程ID及状态(亦即,此行为与未将对SIGCHLD信号的处置置为SIG_IGN时一样)。

sigaction()的SA_NOCLDWAIT标志

SUSv3规定了 SA_NOCLDWAIT 标志,可在调用sigaction()对SIGCHLD信号的处置进行设置时使用此标志。设置该标志的作用类似于将对SIGCHLD的处置置为SIG_IGN时的效果。Linux 2.4及其早期版本并未实现该标志,直至Linux 2.6才实现对其支持。

将对SIGCHLD的处置置为SIG_IGN与采用SA_NOCLDWAIT之间最主要的区别在于,当以 SA_NOCLDWAIT 设置信号处理程序时,SUSv3 并未规定系统在子进程终止时是否向其父进程发送SIGCHLD信号。换言之,当指定SA_NOCLDWAIT时允许系统发送SIGCHLD信号,则应用程序即可捕捉这一信号(尽管由于内核已经丢弃了僵尸进程,造成SIGCHLD处理程序无法用wait()来获得子进程状态)。在包括Linux在内的一些UNIX实现中,内核确实会为父进程产生SIGCHLD信号。而在另一些UNIX实现中,则不会。

当为 SIGCHLD 信号设置 SA_NOCLDWAIT 标志时,老版本 Linux 内核的行为细节同样与SUSv3不符,正如之前在将对SIGCHLD的处置置为SIG_IGN处所讨论的那样。

系统V的SIGCLD信号

Linux 系统中,信号 SIGCLD 与信号 SIGCHLD 意义相同。之所以两个名称并存,是由历史原因造成的。SIGCHLD信号源自BSD,POSIX标准采用了这一名称,同时对BSD信号模型做了大量标准化工作。系统V则提供了相应的SIGCLD信号,在语义上稍许有些不同。

BSD SIGCHLD信号与系统V SIGCLD间的主要差别在于,将信号处置为SIG_IGN时不同的处理方式。

  • 在历史(和一些现代)的BSD实现中,即使忽略了SIGCHLD信号,系统仍会继续将无人等待的子进程变为僵尸进程。
  • 在系统V上,使用signal()(而非sigaction())忽略SIGCLD信号将导致子进程在终止时不会转为僵尸进程。

如前所述,原始的POSIX.1标准对于忽略SIGCHLD的后果未作规定,从而也认可了系统V的行为。而今,系统V的行为已成为SUSv3标准的一部分(不过将仍然使用SIGCHLD的名称)。衍生自系统V的现代系统实现中对该信号使用了 SIGCHLD 这一标准名称,同时继续提供具有相同含义的 SIGCLD 信号。关于 SIGCLD 的更多信息可参考[Stevens & Rago, 2005]。