1、什么是僵尸进程?

僵尸进程是指子进程比父进程先结束,而父进程却没有回收子进程,释放子进程占用的资源。僵尸进程也一直在进程表中占着一个slot,但进程表容量有限,defunct进程不仅占用系统资源,还影响系统性能,如果其数目较多还可能导致系统瘫痪。

这里有个形象的解释:进程在退出后就立刻变成了僵尸,然后等父进程收尸;如果这时它的父进程已经死了,也就是说这个进程是个孤儿,它的父进程临死前”托孤“的init进程,也就是它的养父,会帮它收尸。如果这里它的父进程还没死,就要看这个父进程在做什么了?如果这个父进程对正在调用wait或waitpid操作,则表示这个父进程有准备棺材纸钱,这个进程就可以入土为安了。或者这个父进程设置了SIGCHLD信号处理函数,并在处理函数中执行了wait或waitpid操作,也没问题。但如果父进程忙得不可开交,没有任何收尸的想法,那这个进程就只好一直处于僵尸态,直到父进程什么时候想起来的时候进行wait或waitpid收尸,或者父进程死 的时候收尸。

2、僵尸进程如何产生的?

如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了。那么我们说一个进程终止之后,还保留哪些信息?为什么终止之后还需要保留这些信息呢?

一个进程终止的方法很多,进程终止后有些信息对于父进程和内核还是很有用的,例如进程的ID号、进程的退出状态、进程运行的CPU时间等。因此进程在终止时,回收所有内核分配给它的内存、关闭它打开的所有文件等等,但是还会保留以上极少的信息,以供父进程使用。父进程可以使用 wait/waitpid 等系统调用来为子进程收拾,做一些收尾工作。

因此,一个僵尸进程产生的过程是:父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait() 系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。

3、如何查看僵尸进程?

我们可以使用 ps 等命令查看系统中僵尸进程,僵尸进程的状态标记为‘Z’:

#ps -aux | grep -w 'Z'

4、如何杀死僵尸进程?

#ps -A -o stat,ppid,pid,cmd |grep -e "^[Zz]"    //先查看具体进程: 
#kill -9 pid号         //杀死z进程(这些动作是比较危险的,希望在真正的服务器上面慎用!!!)

 假若你的z进程比较多,可以编写个小小的脚本,下面是参与网上的

#ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -9

5、如何避免僵尸进程

不能使用 kill 后接 SIGKILL 信号这样的命令像杀死普通进程一样杀死僵尸进程,因为僵尸进程是已经死掉的进程,它不能再接收任何信号。事实上,如果系统中僵尸进程并不多的话,我们也无需去消除它们,少数的僵尸进程并不会对系统的性能有什么影响。

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

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

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

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