1.发现
使用top命令检查,发现存在一个僵尸进程。
使用ps命令可以看到更僵尸进程的进程号和对应的父进程信息。
ps -A -ostat,pid,ppid,cmd |grep -e '^[zZ]'
结果如下:
符号 | 含义 |
---|---|
Z | 僵死状态 |
S | 休眠状态 |
D | 不可中断的休眠状态 |
R | 运行状态 |
T | 停止或跟踪状态 |
2.分析
2.1 进程关闭过程
在UNIX系统中,一个进程结束的生命周期大致是这样:
(1)进程退出
进程退出,内核释放该进程的所有资源,包括打开的文件、占用的内存。但也会保留一定的信息,包括进程号(the process ID)、退出状态(the termination status of the process)、运行时间(the amount of CPU time taken by the process)等等。
(2)剩余资源回收
他的父进程调用wait/waitpid来取保留信息的这部分资源,资源全部得到释放,进程结束。
如果父进程先退出,子进程会被init接管,init会成为这个子进程的父进程,子进程退出后init会回收其占用的相关资源。
因此,即使父进程先退出,在UNIX的机制中,也不会产生僵尸进程。
2.2 僵尸进程
定义:
子进程比父进程先结束,父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。
产生原因:
子进程正常退出,如果父进程不调用wait/waitpid,则保留的那段信息不会被释放,其进程号会一直被占用,这也就是僵尸进程
大多数的僵尸进程产生,都是父进程没有调用wait/waitpid来取剩余的资源。
有一种情况值得注意:
子进程后结束,父进程先结束,会产生僵尸进程吗?
答案是不会的,前文已经说过,父进程先退出,子进程会被init接管,由init进程来回收相应的资源。这是UNIX的防止产生僵尸进程的机制。
2.3 危害
僵尸进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。
由于进程表的容量是有限的,所以,defunct进程不仅占用系统的内存资源,影响系统的性能,而且如果其数目太多,还会导致系统瘫痪。
3.处理
处理僵尸进程,这里介绍两种代价小的两种方式:
3.1结束父进程
结束父进程,让子进程成为孤儿进程,init会接手成为子进程的父进程,进而init调用wait/waitpid完成回收。
ps -A -ostat,pid,ppid,cmd |grep -e '^[zZ]'
Kill -9 <ppid>
使用以上命令,基本就能解决问题。
说明:
Kill -9 <pid>
kill -15 <pid>
killall <pid>
一般都不能清除进程,只有kill -9 <ppid> 才能清除僵尸进程。
3.2改写父进程
改写父进程。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。
基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,而此时,尽管对它的默认处理是忽略,如果响应这个消息,可以设置一个处理函数。
4.核查
在本案例中,父进程一直存在,子进程已经是<defunct>状态,说明父进程没有调用wait/waitpid回收资源。
关闭这个父进程,僵尸进程也随即关闭了。