僵死(Zombie)进程
进程在它的生命周期有几种状态:睡眠,可运行,停止,正在运行和僵死状态。所谓僵死进程,指的是一个进程已经退出,它的内存和相关的资源已经被内核释放掉,但是在进程表中这个进程项(entry)还保留着,以便它的父进程得到它的退出状态。一个进程退出时,它的父进程会收到一个SIGCHLD信号。一般情况下,这个信号的句柄通常执行wait系统调用,这样处于僵死状态的进程会被删除。如果父进程没有这么做,结果是什么呢?毫无疑问,进程会处于僵死状态。实际上,僵死进程不会对系统有太大的伤害,最多就是它的进程号(PID)和进程表中的进程项系统不能使用。
ps aux|grep 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)手册页。