僵死进程

APUE 第8章:

如果子进程在父进程之前终止,那么父进程又如何能在做相应检查时得到子进程的终止状态呢?

对此问题的回答是:内核为每个终止子进程保存了一定量的信息,所以当父进程调用wait或者waitpid时,可以得到这些信息,这些信息至少包括进程ID,终止状态,以及该进程使用的CPU时间总量。内核可以释放终止进程所使用的所有存储区,关闭其所有打开的流等。

一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息等)的进程被称为僵死进程(zombie)

 

想到的一个问题就是:终止子进程处于僵死进程直到什么时候为止?

于是google之:

来源: blogs.sun.com/haifeng/entry/unix_%E5%83%B5%E6%AD%BB_zombie_%E8%BF%9B%E7%A8%8B1

进程在它的生命周期有几种状态:睡眠,可运行,停止,正在运行和僵死状态。 所谓僵死进程,指的是一个进程已经退出,它的内存和相关的资源已经被内核释放掉,但是在进程表中这个进程项(entry)还保留着,以便它的父进程得到它 的退出状态。 一个进程退出时,它的父进程会收到一个SIGCHLD信号。一般情况下,这个信号的句柄通常执行wait系统调用,这样处于僵死状态的进程会被删除。 如果父进程没有这么做,结果是什么呢?毫无疑问,进程会处于僵死状态。 实际上,僵死进程不会对系统有太大的伤害,最多就是它的进程号(PID)和进程表中的进程项系统不能使用。(感觉这里说的更加清楚,难道是中文版翻译的不行呵呵)

去掉僵尸进程

在Solaris中,可以用ptree命令列出所有进程,查找进程名称为“defunct”的进程。如果发现了,那么系统存在僵死进程。 注意用kill命令不能杀死这种进程。原因是它已经退出了,什么也没有了,自然无法收到任何信号。

删除僵尸进程的根本方法是让它的父进程调用wait来处理SIGCHLD信号。有两种方式可以做到这一点。一种方法是改变它的父进程。可以用kill命令 杀死它的父进程,这样init变成它的新的父进程,而init会定时地执行wait系统调用。另一种方式是使用调试器,在父进程中执行wait系统调用。 如果知道了父进程的程序名称和它的进程号,运行下面命令:

   %gdb `parent application name` `parent pid`
   (gdb) set unwindonsignal on
   (gdb) call wait(pid of zombie process)

gdb会在父进程中调用wait,从而达到我们的目的。注意,unwindonsignal要被set为on, 它告诉gdb把堆栈恢复到调用wait之前的状态。要不然父进程会crash。 在程序中避免僵死进程 除了显式调用wait或waitpid外,也可以使用下面的代码来避免僵死进程(这儿假设父进程对子进程的状态不感兴趣),它遵循POSIX,是可移植 的。

   struct sigaction sa;
   sa.sa_handler = SIG_IGN;
   sa.sa_flags = SA_NOCLDWAIT;
   sigemptyset (&sa.sa_mask);
   sigaction (SIGCHLD, &sa, NULL);

参考exit(2)手册页。

 

来源: www.chinaunix.net/jh/4/665906.html

一、僵屍進程的産生

当子进程比父进程先运行结束,而父进程没有回收子进程的时候,子进程将成为一个僵尸进程。如果父进程先退出,子进程被init接管,子进程退出后init会回收,就没事了。

二、僵屍進程的危害

僵尸进程是一个运行完毕的进程,所有资源都已经释放了,除了它的进程表项。因此,导致的影响如下:如果操作系统最多能管理1000个进程,那么僵尸进程的存在,将会使得操作系统管理正常进程减少。

三、如何避免僵屍進程的産生

1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起

2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用wait回收

3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号

4. 还有一些技巧,就是fork两次(APUE有介绍),父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收估计还要自己做。