您的位置:首页 > 其它

Unix中的僵尸进程

2015-07-30 10:18 169 查看
现实中的僵尸是一种似死非死的状态——一直在人间四处游走,但是却不能做一些活人能做的事情。

而对于僵尸进程,与这个有些类似,它不能像一般进程那样来执行程序、被调度而占用CPU,但是它却不能消失,因为它还没将它的“死讯”告诉它的父进程和系统,它得让别人知道它已经死了,免得别人还来找它干活它却啥都不能干。

我们需要了解到,进程的终止是一件异步事件,即是一件可能随时都会发生的事情。进程有8种终止方式,包括5种正常终止方式和3种非正常终止方式。当一个进程终止的时候,当然,我们的内核肯定是会知道这件事情的(因为这几种终止方式总是离不开信号或者系统调用)。当内核发现有进程终止时,它将向终止进程的父进程发送一个SIGCHLD信号,告诉该终止进程的父进程,你的几号子进程终止了。当然,子进程终止了,它肯定想要告诉父进程它的“死因”是什么,这里的"死因"就是子进程的终止状态。如果子进程是正常终止,则有子进程自己将终止状态返回给父进程;如果子进程是非正常终止的,还没来得及传送终止状态,则由内核(不是子进程本身)产生一个指示其异常终止原因的终止状态。而我们的父进程获取这些终止状态也要主动的,是通过主动调用wait()或者waitpid()函数来获得的,当然这两个函数也将获取到子进程死前的一些信息,包括其进程ID,其使用的CPU时间总量等。那么问题来了,我们的父进程是什么时候调用wait()或者waitpid()来获取这些终止状态的呢?是在子进程死后(内核发送SIGCHLD信号),还是在子进程没死的时候呢?当然,在死后调用函数才是正确的,如果在进程还没有终止的时候就调用函数则会发生什么呢?这个问题大了,我们(子进程)都还好好的,你(父进程)就来求死讯,这不是咒我们吗?好吧,你这么坏,就罚你不准动,在我们还活着的这段时间里都好好等着吧。也就是说,父进程在所有进程都在运行的时候就调用函数,则会导致父进程陷入阻塞状态,直到有一个子进程终止,父进程才能被再次调度。如果是在子进程终止后调用函数,则就可以顺利取得该终止子进程的终止状态和一些终止信息然后立即返回,在这里,父进程调用函数的最好地点就是在SIGCHLD信号的处理函数中了。最后还有一种情况,如果一个进程没有子进程,那么调用wait()等函数则会出错返回。

好了,说了这么多也没有说到主题上,到现在都还没有提到僵尸进程是啥。囧,实在是细节太多,写着写着就容易跑偏了。好了,从上面了解了进程终止时发生的一些事情,我们可以比较清楚地了解到,父进程是通过显式调用wait()或者waitpid()来获取终止子进程的终止状态信息的,通俗点说就是,父进程是得显式地调用一个函数来为我们已经终止的子进程"收尸"的,如果父进程没有显式调用这些函数,会导致父进程不能在子进程终止后的第一时间内就将其"收尸",这就导致在子进程死后有一段时间是以终止的状态存在在系统中的,这种状态的子进程我们成为"僵尸进程",而这个时间段就是"子进程死后到父进程显式调用wait等函数获取终止子进程终止状态信息"的时间段。好吧,应该了解僵尸进程的含义了吧,下面通过一个示例来展示一下僵尸进程。

代码和运行界面如下:





如上面的代码,我们创建了一个子进程,然后让父进程sleep(),让子进程先执行。在子进程里面直接终止自身,即这个效果的过程应该是,在父进程运行的时候,子进程已经终止了,并且没有调用wait()或者waitpid()函数来获取终止子进程的终止状态和信息,于是在子进程终止后父进程终止前的这段时间里,子进程是一个僵尸进程。我们可以从另外一个终端里面使用ps命令查看当前系统中的僵尸进程,查看结果如下:



从上面的运行结果可以看出,系统中存在一个僵尸进程,就是我们刚刚创建的子进程。

在上面的程序中,我们编写了sleep函数使得子进程先于父进程执行完,但是究竟是哪个进程先执行,这个与系统的调度算法相关。所以有种情况可能会发生——父进程先于子进程终止。要知道,上面说的那么多,都是要靠父进程才能完成的,现在父进程先终止了,谁来为子进程的终止善后呢?现实生活中没有了双亲的孩子就是孤儿了,我们姑且将这种子进程成为孤儿进程吧。孤儿一般都是由孤儿院收养,孤儿院的院长就是他们的继父继母了。在内核中也有个"孤儿院院长进程",就是init进程,这个进程会收养内核中所有没有父进程的孤儿进程,成为它们的父进程,这样,就可以保证内核中所有的进程都有父进程了。inti进程是如何收养的呢?其实init进程一直都很忙,只要内核中有进程终止,init进程都要去查看一下这个终止进程是否还存在子进程,如果有的话就收养它们,成为它们的新父进程。另外还有最后一点,init进程有些不一样,它不像普通父进程那么磨蹭,慢慢地去调用wait来获取终止子进程状态和信息,init进程帅气多了,每当内核中有进程终止,init进程都会调用wait函数取得其终止状态,这样init进程真的很忙,已有进程终止,既要领养终止进程的子进程,又要调用wait来获取其终止状态,但是它这么做却是保证了内核中的孤儿进程都有父进程,别且防止了内核中被塞满了僵尸进程。nice。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: