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会回收。不过子进程的回收估计还要自己做。