僵尸进程


在unix/linux系统中,如果一个进程执行已经结束,但是它所占用的资源没有完全释放,那么这个进程就叫做僵尸进程(Zombie process or defunct process)。那么为什么会出现僵尸进程呢?我们都知道,在unix/linux中,除了进程ID为0的进程(swapper进程)没有父进程,其余的进程都有父进程,也就是说这些进程都是某个进程的子进程。在子进程的状态发生改变时(如从运行态(running)变为暂停(stop),或由stop继续运行(continue),或者进程终止),运行父进程通过wait/waitpid/wait3/wait4函数获取子进程的状态。这样子进程终止(比如通过return语句或exit语句)后,进程空间占用的存储空间(如正文段、数据段、bss段等)都释放了,但是内核中为这个子进程维护的进程表却没有释放,直到父进程获取了子进程的终止状态后,系统才会释放掉内核中的子进程所占用的资源空间。可以看出,所谓僵尸进程没有释放的资源,并不是进程空间自身的资源,而是系统维护的进程对应的资源。

       从上面的分析来看,僵尸进程并不一定是坏事。事实上,任何进程在进程终止到父进程获取它的终止状态、内核最后释放进程所占用的所有资源这段时间内,进程都处于僵尸状态,僵尸进程可以看作是进程生命周期的必需的一个阶段,因为如果在父进程没有获取子进程的状态之前内核就把子进程彻底清除掉,那么父进程就永远无法知道子进程的相关信息了,连子进程是否还存在不都不知道,更不用说子进程的返回值了,这样我们很多功能是不能很好的实现的。我们还可能故意的设计出僵尸进程,当父进程fork出很多子进程时,故意延迟获取终止子进程的状态。这样处于僵尸状态的子进程的pid不会释放到可用的pid资源池中,就可以避免刚终止的子进程的pid又被分配给新fork出的子进程。我们需要避免的是非出于设计目的的或长时间的僵尸进程,避免资源泄露(resource leak)。


      那么在设计时有哪些方法可以避免僵尸进程呢?

      1、在父进程调用wait或waitpid等待子进程终止。

            这种方式的缺点是父进程调用wait或waitpid时,如果子进程没有终止,则进入休眠,从而无法处理其他任务。或者如果父进程的任务较多,可能长时间无法调用wait、waitpid函数。

       2、父进程捕获SIGCHLD信号,在信号处理函数中调用wait或waitpid函数。

             这样父进程获取子进程的终止状态是异步的,子进程没有终止时,父进程可以正常处理任务;子进程终止后,父进程才终止当前任务的执行,读取子进程终止状态,从而释放子进程的资源,然后继续暂停的任务,响应也比较及时。

       3、如果父进程不需要子进程的终止状态,可以在fork子进程之前注册SIGCHLD信号的处理方式为忽视(ignore)。

             可以通过signal函数signal(SIGCHLD,SIG_IGN)或处理SIGCHLD信号的sigaction函数的sa_flag设置为SA_NOCLDWAIT。父进程忽视SIGCHLD信号,相当于告诉系统不关心这个信号,系统就可以直接释放僵尸进程的资源了

       4、两次fork产生子进程。

            父进程先fork产生子进程,子进程什么都不做,只fork产生自己的子进程,然后直接exit退出,需要处理的任务在孙子进程中处理。这样父进程只要wait子进程就可以了。而子进程产生的孙子进程(们)由于子进程的终止而变为孤儿进程,从而被init进程领养(inherited by init process),init进程被设计成总是为终止的子进程调用wait函数,从而防止僵尸进程的产生。有的人可能要问,这种方法父进程不是照样需要wait子进程的终止吗?但是父进程需要fork很多子进程时,父进程可以只fork一个子进程,而真正执行任务的进程都由它的子进程创建,这样父进程只要wait一个进程就可以了,而且wait的进程因为创建完子进程就结束了,wait的时间比较容易预见,不影响父进程处理其他任务。只是这里需要注意的是,因为子进程创建了孙子进程后,孙子进程与子进程谁先执行不是固定的,需要保证子进程在孙子进程终止前先终止,因为子进程并没有wait孙子进程的终止状态,如果孙子进程没有成为孤儿进程就终止了,同样变为僵尸进程。当然,实际情况中,孙子进程因为要处理任务,必子进程先终止的可能性比较小。


       如果进程运行后,发现产生了僵尸进程,如何kill僵尸进程呢?

       僵尸进程因为进程空间已经释放了,已经无法执行了,所以无法通过kill命令给僵尸进程发送信号使僵尸进程终止。

       如果父进程设计时有wait子进程终止的处理,只是由于设计不当没有正常处理(如信号处理不可靠,SIGCHLD信号丢失),可以尝试通过kill给父进程发送SIGCHLD信号。

      如果父进程可以终止,也可以终止父进程。因为父进程终止后,它所有的子进程(包括僵尸进程)的父进程都变为init进程(pid值为1),init进程总是会为终止的进程调用wait函数


参考

《unix环境高级编程》w.Richard.Stevens

http://en.wikipedia.org/wiki/Zombie_process

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值