僵尸进程 vs 孤儿进程

84

当父进程在子进程退出后没有使用wait系统调用来读取其退出状态时,将会创建出一个僵尸进程;而孤儿进程则是指当原始父进程在子进程之前终止,init将回收该子进程。

在内存管理和进程表方面,这些进程在UNIX中是如何处理的?

有哪些例子或极端情况,会对整个应用程序或系统造成僵尸进程或孤儿进程的创建有害?


7
制造大量僵尸程序并观察您的系统变得极其缓慢。 - dbarnes
5
一个僵尸进程没有理由去消耗(大量的)内存...它已经死了。基本上,它只是一个占位符,以便父进程在未来某个时候仍然可以读取其退出状态。 - FatalError
7
这里的 "zombie" 并未占用任何重要的内存或资源,它只是一个等待被处理的“退出状态”。而 "孤儿进程" 则与其他正在运行的进程一样——只是名字有些奇怪。请注意,此处不含解释说明。 - Clinton Pierce
@clintp 但是孤儿进程完成后,操作系统会处理它,所以它不可能成为僵尸进程,对吧? - MarkAWard
1
如果在“init”过程中有错误或内核错误导致“init”无法正常工作,则孤立进程可能会变成僵尸进程。否则不会。但从另一个角度来看,每个退出的进程都是僵尸进程,直到父进程将其清理干净,所以它们确实会变成僵尸进程,但时间很短。 - user2404501
“孤儿进程是指被回收的子进程,因此它不再是孤儿!由于在父进程死亡后进程总是被回收,所以永远不会有孤儿进程。” - alk
9个回答

166
当子进程退出时,必须有进程“等待”它,并获取其退出代码。在此之前,该退出代码将存储在进程表中。读取该退出代码的行为称为“收割”子进程。从子进程退出到被收割的时间段,它被称为僵尸进程。(当你考虑它时,整个命名法有点可怕;我建议不要想太多。)
僵尸进程仅占用进程表中的空间。它们不占用内存或CPU。然而,进程表是有限的资源,过多的僵尸进程可能会填满它,导致无法启动其他进程。除此之外,它们也会产生麻烦的混乱,并应该尽量避免。
如果一个进程退出时,还有它的子进程正在运行(并且没有杀死它的子进程;比喻依然很奇怪),那么这些子进程就是孤儿进程。孤儿进程会立即被“init”收养(实际上,我认为大多数人称其为“重新归属”,但“收养”似乎更好地传达了这个隐喻)。孤儿进程只是一个进程。它将使用任何它所使用的资源。说它是“孤儿”并不合理,因为它有父进程,但我经常听到人们这样称呼它们。
“init”自动收割其子进程(无论是否被收养)。因此,如果你退出而没有清理你的子进程,那么它们将不会成为僵尸进程(至少不会超过一瞬间)。
但是存在长时间存活的僵尸进程。它们是现有进程的前代子进程,其父进程没有将它们收割。该进程可能已挂起。或者它可能编写得很差,忘记了收割它的子进程。或者它可能负载过重,还没有来得及收割。或其他原因。但由于某种原因,父进程仍然存在(因此它们不是孤儿),它们没有被等待,因此它们在进程表中继续存在,成为僵尸进程。
因此,如果你看到僵尸进程持续存在超过一瞬间,则意味着父进程存在问题,应该采取措施改善该程序。

32
当一个进程终止时,它的资源会被操作系统释放。但是,由于进程表中包含进程的退出状态,因此该进程在父进程调用wait()之前必须保留其条目。已经终止但其父进程尚未调用wait()的进程称为“僵尸进程”。所有进程在终止时都会转换到这个状态,但通常只存在很短的时间。一旦父进程调用wait(),僵尸进程的进程标识符和进程表中的条目将被释放。
现在考虑一下如果父进程没有调用wait(),而是自行终止,从而使其子进程成为“孤儿进程”,会发生什么情况。Linux和UNIX通过将init进程指定为孤儿进程的新父进程来处理这种情况。init进程定期调用wait(),因此允许收集任何孤立进程的退出状态,并释放孤立进程的进程标识符和进程表条目。
来源:操作系统概念(Abraham, Peter, Greg)

2
真的很喜欢这个简短而精练的回答。 - Azim
1
实际上,没有孤儿,因为它们会被收养。 - alk

27

孤儿进程是指其父进程已经完成或终止,但它(子进程)仍然在运行。
僵尸进程或者称为“死进程”,是一种已经完成执行但由于其父进程没有调用wait()系统调用而在进程表中仍然存在的进程。


6

孤儿进程 - 当父进程退出时,初始化进程成为子进程的父进程。 每当子进程终止时,操作系统都会删除进程表。

僵尸进程 - 当子进程终止时,它会向父进程发送退出状态。 与此同时,假设您的父进程处于睡眠状态,并且无法接收来自子进程的任何状态。 虽然子进程已经退出,但是该进程在进程表中占用空间。

在 Linux Ubuntu 中执行此命令 >> ps -eo pid,ppid,status,cmd

如果您发现最后一行有defunc,即表示您的进程处于僵尸状态并且正在占用空间。


5

僵尸进程: 已经执行完毕但仍有一条记录在进程表中向其父进程报告的进程称为僵尸进程。子进程被从进程表中移除之前,通常会先变成僵尸进程。父进程读取子进程的退出状态并清除进程表中该子进程的条目。

孤儿进程: 其父进程不存在了,即已经完成或终止而没有等待其子进程结束的进程称为孤儿进程。


3
  1. 仅在使用PID 1的进程中才存在孤儿进程。

    从正在运行的进程的角度来看,它是否直接启动并因此具有PID 1作为父进程或者被PID 1继承(因为其原始父进程与PID 1不同)而获得PID 1,对于它来说没有区别。它像任何其他进程一样处理。

  2. 每个进程在结束时都会经历某种僵尸状态,即在发出SIGCHLD并且已经确认其处理(传递或忽略)之间的阶段。

当进入僵尸状态时,该进程只是系统进程列表中的一个条目。

僵尸进程独占的唯一重要资源是有效的PID。


3

我想添加两个代码片段,分别涉及孤儿进程和僵尸进程。但首先,我将发布在Silberschatz、Galvin和Gagn的书“操作系统概念”中所述的这些进程的定义:

如果没有父进程等待(未调用wait())进程,则为 僵尸进程

如果父进程终止而没有调用wait,则该进程是一个孤儿进程

孤儿进程

// A C program to demonstrate Orphan Process.  
// Parent process finishes execution while the 
// child process is running. The child process 
// becomes orphan. 

#include <stdio.h>  //printf
#include <stdlib.h> //exit
#include <sys/types.h> //fork
#include <unistd.h> //fork and sleep
  
int main() 
{ 
    // Fork returns process id 
    // in parent process 
    pid_t child_pid = fork(); 
  
    // Parent process didn't use wait and finished before child
    // so the child becomes an orphan process

    // Parent process 
    if (child_pid > 0) {
        printf("I finished my execution before my child"); 
    }
    else // Child process 
        if (child_pid == 0) { 
            sleep(1); //sleep for 1 second
            printf("This printf will not be executed"); 
        } 
        else{
            //error occurred
        }
  
    return 0; 
}

输出

我在我的孩子之前完成了执行

僵尸进程

// A C program to demonstrate Zombie Process. 
// Child becomes Zombie as parent is not waiting
// when child process exits. 

#include <stdio.h>  //printf
#include <stdlib.h> //exit
#include <sys/types.h> //fork
#include <unistd.h> //fork and sleep

int main() 
{ 
    // Fork returns process id 
    // in parent process 
    pid_t child_pid = fork(); 
 
    // Parent process didn't use wait 
    // so the child becomes a zombie process

    // Parent process 
    if (child_pid > 0){ 
        sleep(1); //sleep for 1 second
        printf("\nI don't wait for my child");
    }
    else // Child process 
        if(child_pid == 0){ 
            printf("My parent doesn't wait me");
            exit(0);
        }
        else{
            //error occurred
        }
    
    return 0; 
} 

输出

我的父进程不等我

我不等待我的子进程

编辑:参照并灵感来自这里


2

摘要

  • 僵尸进程是一个已经执行完毕但在进程表中仍然有条目的子进程,等待其父进程确认其终止。
  • 孤儿进程是一个子进程,在其父进程之前终止,并被init进程接管。

僵尸进程会发生什么?
使用wait()系统调用来清除僵尸进程,它确保父进程在子进程完成之前不执行或空闲等待。
当子进程执行完毕时,父进程会从进程表中移除子进程的条目。这被称为“收割子进程”。

enter image description here

孤儿进程会发生什么?

  • 在现实世界中,孤儿会被监护人收养并照顾。
  • 类似地,在Linux中,孤儿进程会被一个新的进程收养,通常是init进程(pid=1)。这称为重新分配父进程。
  • 当内核检测到操作系统中的孤儿进程时,通过重新分配父进程来完成。
  • 新的父进程向内核请求清理孤儿进程的PCB,并等待子进程执行完毕。

enter image description here


僵尸进程是指已完成任务但仍在进程表中显示条目的进程。
孤儿进程是指在父进程终止或完成后仍然运行的子进程,而不等待子进程执行。
僵尸进程状态始终由“Z”表示。
孤儿进程是由于系统崩溃而无意中创建的。
僵尸进程被视为死亡,不用于系统处理。
孤儿进程是计算机进程,即使在其父进程终止init之后,它仍然成为父进程并继续剩余的任务。
使用wait()系统调用来处理僵尸进程。
内核为孤儿进程分配一个新的父进程。通常,新的父进程是init进程(pid=1)。
要删除僵尸进程,请执行kill命令。
使用SIGHUP信号终止孤儿进程。

来源:


2

已经执行完毕但仍然在进程表中保留有条目向其父进程报告的进程被称为僵尸进程。如果父进程不存在,即已完成或终止而没有等待其子进程终止,则称为孤儿进程。

最初的回答:

僵尸进程是指已经执行完毕但仍然在进程表中保留有条目向其父进程报告的进程。孤儿进程则是指父进程不存在,即已完成或终止而没有等待其子进程终止的进程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接