当一个进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()取得子进程的终止状态。如果进程不调用wait/waitpid的话,那么系统保留的那段子进程信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
孤儿进程是没有父进程的进程,孤儿进程这个重任就落在了init进程身上,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,因此孤儿进程并不会有什么危害。
Linux允许进程查询内核以获得父进程的PID,或者其任何子进程的执行状态。例如,进程可以创建一个子进程来执行特定的任务,然后调用诸如wait()/waitpid()这样的库函数检查子进程是否终止。如果子进程已经终止,那么,它的终止代号将告诉父进程这个任务是否已成功地完成。
为了遵循这些设计原则,不允许Linux内核在进程一终止后就丢弃包含在进程描述符字段中的数据。只有父进程发出了与被终止的进程相关的wait()类系统调用之后,才允许这样做。这就是引入僵死状态的原因:尽管从技术上说进程已死,但必须保存它的描述符,直到父进程调用得到通知。
如果一个进程已经终止,但是它的父进程尚未调用wait()或waitpid()对它进行处理,这时的进程状态称为僵死状态,处于僵死状态的进程称为僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。
僵尸进程是如何产生的
在UNIX系统中,一个进程结束了,但是它的父进程没有等待(调用wait/waitpid)它,那么它将变成一个僵尸进程。通过ps命令查看其带有defunct的标志。僵尸进程是一个早已结束的进程,但在进程表(process table)中仍占了一个位置(slot)。
但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中运行的所有进程,看看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就有init进程接管它,称为它的父进程,从而保证每个进程都会有一个父进程。而init进程会自动wait其子进程,因此被init接管的所有进程都不会变成僵尸进程。
只有wait了子进程,并最终释放他们占用的系统进程表中的资源和进程号,这样,这些僵死的孤儿进程就彻底被清除了。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符(PCB)仍然保存在系统中。这种进程称之为僵尸进程。
任何一个子进程(init除外)在exit之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时处理,可能用ps就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
孤儿进程是没有父进程的进程,孤儿进程这个重任就落在了init进程身上,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,因此孤儿进程并不会有什么危害。