【Linux】僵尸进程的检测,清理和避免

一.僵尸进程的产生

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

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

二.检测系统中的僵尸进程

1.我们可以通过命令top来初步查看系统中僵尸进程的数目:

$ top
top - 09:58:31 up 3 min,  2 users,  load average: 0.76, 0.45, 0.19
Tasks: 212 total,   1 running, 210 sleeping,   0 stopped,   1 zombie
%Cpu(s):  6.4 us,  3.1 sy,  0.6 ni, 78.6 id, 11.2 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   4037576 total,  1664952 used,  2372624 free,    82416 buffers
KiB Swap:  1998844 total,        0 used,  1998844 free.   916128 cached Mem

可以看到,我们系统中有一个僵尸进程(1 zombie)。

查看具体信息可以用ps(僵尸进程的stat为Z):

$ ps aux | grep -w 'Z'
#或者只查看特定的栏目:
$ ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'

一般僵尸进程很难直接用kill杀死,因为僵尸进程是已经死掉的进程,它不能再接收任何信号。

一个可选的解决方法是,杀死父进程(需要谨慎……),于是僵尸进程成为”孤儿进程”,它由给1号进程init收养,init 进程会周期性地去调用 wait 系统调用来清除它的僵尸孩子。因此,你会发现父进程死掉之后,僵尸进程也跟着消失,其实是 init 进程在为其收尸!

三.避免产生僵尸进程

1.一般,为了防止产生僵尸进程,在fork子进程之后我们都要wait它们;同时,当子进程退出的时候,内核都会给父进程一个SIGCHLD信号,所以我们可以建立一个捕获SIGCHLD信号的信号处理函数,在函数体中调用wait(或waitpid),就可以清理退出的子进程以达到防止僵尸进程的目的。
(注意,建立信号处理函数并在其中调用wait并不足以防止出现僵尸进程,其原因在于:可能有多个信号在信号处理函数执行之前产生,而信号处理函数只执行一次,因为Unix信号一般是不排队的!正确的解决办法是调用waitpid而不是wait,这个办法为:信号处理函数中,在一个循环内调用waitpid,以获取所有已终止子进程的状态。我们必须指定WNOHANG选项,他告知waitpid在有尚未终止的子进程在运行时不要阻塞。)

2.在产生子进程的时候使用两次fork(),而且紧跟着使子进程直接退出,于是孙子进程成为孤儿进程,从而init进程将负责清除这个孤儿进程。(当然,这里需要注意,父进程仍然需要循环调用waitpid来等待子进程的结束,我们调用2次fork的好处是,孙子进程与父进程脱离了关系,子进程fork之后立即返回了,所有任务都交给了孙子进程去完成,这样一来,父进程就基本不用花时间在等待子进程上了)

例子:

    /* create a new process and return if not the child */
    if ( fork() != 0 )
    {
        while (waitpid(-1, NULL, 0) > 0);   /*wait for all children*/
        return;
    }

    /*avoid zombie*/
    if ( fork() != 0)
        exit(0);
    /*
    **do something
    */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值