0%

面试知识点详细解读之僵尸进程和孤儿进程

什么是僵尸进程?

简单的说,僵尸进程就是子进程先于父进程退出,并且父进程并没有调用wait系统调用,即使其进程映像中占用的系统资源(比如内存,页表等)都会被释放回收,但这时子进程的进程描述符(PCB)结构仍然存在于系统中得不到释放,就称这样的进程为僵尸进程(Zombie)。

为什么会产生僵尸进程?
1
2
void exit(int status);
pid_t wait(int* status);

首先,如果一个进程不成为僵尸进程的话,就要请求内核来回收它最后遗留的PCB。而内核给出了exitwait系统调用来提供回收进程PCB的途径。进程到最后或者是主动调用exit结束运行,或者是通过return语句使得编译器安插exit调用代码,目的都是向父进程传递一个“退出状态值”(这个状态值就是exit函数的那个参数,或者是return语句后面那个整数),我们就叫它临终遗言吧。这个遗言就保存在进程遗留的PCB中,等待着父进程调用wait系统调用去取它的遗言,也就是内核会把这个遗言放在wait函数的参数所指向的内存,取完之后,内核就会回收这块PCB资源。这些都是在执行系统调用期间发生的,也就是说调用了这两个系统调用,给了内核机会来回收遗留的PCB

如果父进程在派生出子进程后并没有调用wait等待接收子进程的返回值,这时某个子进程调用exit退出了,自然没人来接收返回值了。因此其PCB所占的空间不能释放,没人为其“收尸”,自然就成了“僵 尸”。

僵尸进程有什么危害以及如何处理?

虽然进程的退出状态未被父进程取出前,除了PCB以外,其他所有资源都可以释放。但由于PCB不释放,它原本的pid也会继续被占用,当僵尸进程数量很大时,系统将无可用pid分配给新进程,从而加载进程失败。

既然产生了僵尸进程,就说明父进程违背了和内核的约定,不想回收子进程。那么,内核也提供了毫不客气的办法来处理,那就是直接kill掉父进程,在Linux中可以利用ps –ef查看所有任务的pidppid,找到状态为Z的进程,查看其ppid,跟着向pidppid的进程发送kill -9

什么是孤儿进程?

在子进程提交给父进程返回值的时候,有这样一种情况,当父进程提前退出时,它所有的子进程还在运行,没有一个执行了exit,因为它们的生命周期尚未结束,还在运行中,个个都拥有“全尸”(完整的进程映像), 这些进程就称为孤儿进程。这时候所有的子进程会被pid1init进程收养,init进程会成为这些子进程的新父亲,当子进程退出时会由init负责为其“收尸”。init进程是所有进程的祖先进程,所有进程最初都是通过它派生出来的。

对系统而言,有了init进程的“收养“,孤儿进程并没有什么危害,init会很好地为其善后,因此并不会额外占用资源,它和普通的进程一样,原理上对系统不会产生不良影响。

总结

exit是由子进程调用的,表面上功能是使子进程结束运行并传递返回值给内核,本质上是内核在幕后会将进程除PCB以外的所有资源都回收。wait是父进程调用的,表面上功能是使父进程阻塞自己,直到子进程调用exit结束运行,然后获得子进程的返回值,本质上是内核在幕后将子进程的返回值传递给父进程并会唤醒父进程,然后将子进程的PCB回收。

其它可参考链接

孤儿进程与僵尸进程产生及其处理_Eunice_fan1207的博客-CSDN博客

Linux下僵尸进程的处理回收详解_CSer-CSDN博客