僵尸进程产生的原因、危害,与孤儿进程的区别

本文深入探讨了Linux系统中的僵尸进程和孤儿进程的概念。解释了僵尸进程的产生原因、危害及处理方法,以及孤儿进程的定义和无害性。并通过实例演示了如何通过wait()和waitpid()函数有效处理僵尸进程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 僵尸进程

1.1 为什么会有僵尸进程

         Linux 允许进程查询内核以获得其父进程的 PID,或者其任何子进程的执行状态。例如,进程可以创建一个子进程来执行特定的任务,然后调用诸如 wait() 这样的一些库函数检查子进程是否终止。如果子进程已经终止,那么,它的终止代号将告诉父进程这个任务是否已成功地完成。
         为了遵循这些设计原则,不允许 Linux 内核在进程一终止后就丢弃包含在进程描述符字段中的数据。只有父进程发出了与被终止的进程相关的 wait() 类系统调用之后,才允许这样做。这就是引入僵死状态的原因:尽管从技术上来说进程已死,但必须保存它的描述符,直到父进程得到通知。
         如果一个进程已经终止,但是它的父进程尚未调用 wait() 或 waitpid() 对它进行清理,这时的进程状态称为僵死状态,处于僵死状态的进程称为僵尸进程(zombie process)。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。

1.2 僵尸进程是如何产生的

         在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。通过ps命令查看其带有defunct的标志。僵尸进程是一个早已死亡的进程,但在进程表 (processs table)中仍占了一个位置(slot)。但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个 进程是刚刚结束的这个进程的子进程,如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动 wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。为了观察到僵尸进程,我们自己写一个不正常的程序,父进程 fork 出子进程,子进程终止,而父进程既不终止也不调用 wait 清理子进程:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
  int i = 100;
  pid_t pid=fork();
  if(pid < 0)
  {
    perror("fork failed.");
    exit(1);
  }
  if(pid > 0)
  {
    printf("This is the parent process. My PID is %d.\n", getpid());
    for(; i > 0; i--)
    {
      sleep(1);
    }
  }
  else if(pid == 0)
  {
    printf("This is the child process. My PID is: %d. My PPID is: %d.\n", getpid(), getppid());
  }
  return 0;
}

把上面的代码保存到文件 zomprocdemo.c 文件中,并执行下面的命令编译:

$ gcc zomprocdemo.c -o zomprocdemo

然后运行编译出来的 zomprocdemo 程序:

$ ./zomprocdemo

在这里插入图片描述

此时子进程已经退出,但是父进程没有退出也没有通过 wait() 调用处理子进程。我们使用 ps 命令查看进程的状态:
在这里插入图片描述
       上图红框中的大写字母 “Z” 说明 PID 为 112712 的进程此时处于僵死的状态。让我们接着往下看!在结束 sleep 后父进程退出。当父进程退出后,子进程会变成孤儿进程,此时它会被一个管理进程收养。在不同的系统中,这个管理进程不太一样,早期一般是 init 进程,Ubuntu 上是 upstart,还有近来的 Systemd。但是它们都完成相同的任务,就是 wiat() 这些孤儿进程,并最终释放它们占用的系统进程表中的资源。这样,这些已经僵死的孤儿进程就彻底的被清除了。

1.3僵尸进程的危害

       在进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号 PID,退出状态 the termination status of the process,运行时间 the amount of CPU time taken by the process 等)。直到父进程通过 wait / waitpid 来取时才释放。

       如果进程不调用 wait / waitpid 的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。

1.4 如何处理僵尸进程

       僵尸进程的产生是因为父进程没有 wait() 子进程。所以如果我们自己写程序的话一定要在父进程中通过 wait() 来避免僵尸进程的产生。当系统中出现了僵尸进程时,我们是无法通过 kill 命令把它清除掉的。但是我们可以杀死它的父进程,让它变成孤儿进程,并进一步被系统中管理孤儿进程的进程收养并清理。

下面的 demo 中,父进程通过 wait() 等待子进程结束:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
  pid_t pid;
  pid = fork();
  if (pid < 0)
  {
    perror("fork failed");
    exit(1);
  }
  if (pid == 0) {
    int i;
     for (i = 3; i > 0; i--)
     {
      printf("This is the child\n");
      sleep(1);
     }
     // exit with code 3 for test.
    exit(3);
  }
  else
  {
    int stat_val;
    wait(&stat_val);
     if (WIFEXITED(stat_val))
     {
       printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
     }      
  }
  return 0;
}

demo 中父进程不仅等待子进程结束,还通过 WEXITSTATUS 宏取到了子进程的 exit code。
来源:https://www.jb51.net/article/135562.htm

2 孤儿进程

        孤儿进程指:当一个子进程的父进程结束时,子进程还在运行,此时,这个子进程就叫做孤儿进程。最后,孤儿进程会被init进程(进程id为1)进程收养,即将init作为孤儿进程的父进程,当子进程结束时,由init进程完成对它的状态收集工作。因此,孤儿进程是无害的。
看一个例子:在这里插入图片描述
再看下运行结果:

在这里插入图片描述
来源:https://blog.csdn.net/L__xing/article/details/79670002

3 wait() waitpid()的区别

wait和waitpid函数是用来处理僵死进程的。他们有一些区别,用的时候老是忘,就打算做个实验测试一下。也供像我这样UNIX网络编程新手学习参考。

  1. 使用背景
  2. 函数原型
  3. wait和waitpid区别

1、使用背景——什么是僵死进程(zombie)?
       zombie的作用是维护子程序的信息,以便父程序在以后能够调用它。这些信息包括:进行ID,终止状态、资源利用率(CPU利用率、内存使用等等)。(《UNIX网络编程》P106)但是操作系统限制了某一时刻能同时存在的进程的最大数目,虽然说它们不占有CPU,不占有内存,但是当下次产生新进程的时候,就会产生未知的错误。因此,有效的处理僵死进程,是有必要的一些事。
  这个时候,就用到了wait和waitpid两个函数。返回已经终止的子进程的进程ID号,并清除僵死进程

2、函数原型

#include <sys/wait.h>
pid_t wait(int * statloc);
pid_t waitpid(pid_t pid,int *statloc,int options);

成功返回进程ID,出错返回0或者-1。

3、wait和waitpid区别
wait会令调用者阻塞直至某个子进程终止;
waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程。

waitpid的option常量:

  1. WNOHANG waitpid将不阻塞如果指定的pid并未结束
  2. WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

waitpid中pid的含义依据其具体值而变:

  1. pid==-1 等待任何一个子进程,此时waitpid的作用与wait相同
  2. pid >0 等待进程ID与pid值相同的子进程
  3. pid==0 等待与调用者进程组ID相同的任意子进程
  4. pid<-1 等待进程组ID与pid绝对值相等的任意子进程

waitpid提供了wait所没有的三个特性:

  1. waitpid使我们可以等待指定的进程
  2. waitpid提供了一个无阻塞的wait
  3. waitpid支持工作控制

来源:https://www.cnblogs.com/yusenwu/p/4655286.html

<think>好的,用户想了解Linux或Unix系统中僵尸进程孤儿进程区别。首先,我需要回忆这两个概念的基本定义。 僵尸进程是已经终止但父进程尚未通过wait()系统调用获取其退出状态的进程。这时候,进程描述符仍然保留在系统中,导致资源泄漏。记得引用中的说明,如果父进程不调用wait(),就会产生僵尸进程[^2]。僵尸进程无法被kill命令终止,因为它们已经死了,只能等待父进程处理。 孤儿进程则是父进程已经终止或退出,而子进程仍在运行的进程。这时候,init进程(PID 1)会接管这些子进程,成为它们的新父进程。这样,孤儿进程最终会被init回收,不会长期占用资源。例如,用户提到的引用[1]中的例子,父进程先于子进程退出,子进程就变成了孤儿进程,由init接管[^1]。 接下来需要比较两者的生命周期和影响。僵尸进程会一直存在直到父进程调用wait(),如果父进程不处理,就会一直残留,占用系统资源。而孤儿进程只是暂时存在,被init接管后会被正常回收,不会造成长期问题。 另外,处理方式也不同。解决僵尸进程需要父进程正确处理子进程的退出状态,或者终止父进程,让init来回收。而孤儿进程则由系统自动处理,无需用户干预。 可能还需要提到如何检测这两种进程。使用ps命令查看状态,僵尸进程显示为Z,而孤儿进程的父进程ID会变为1。 用户可能还会关心实际应用中的例子,比如编写代码时,如果父进程没有正确等待子进程,就会产生僵尸进程。而如果父进程意外终止,子进程变成孤儿进程,由init接管。 最后,总结两者的主要区别产生原因、处理方式、对系统的影响以及如何预防和解决。需要确保回答清晰,结构合理,符合用户的需求。</think>### 僵尸进程孤儿进程区别 #### 1. **僵尸进程(Zombie Process)** - **定义**: 子进程已终止但未被父进程通过`wait()`系统调用回收退出状态,此时进程描述符仍保留在系统中。 - **特征**: - 状态为`Z`(通过`ps`命令查看) - 无法被终止(已处于终止状态) - 占用少量系统资源(如进程ID) - **产生原因**: 父进程未调用`wait()`或`waitpid()`,导致操作系统无法释放子进程资源。 - **解决方法**: - 修改父进程代码,添加`wait()`调用 - 终止父进程僵尸进程会被`init`进程接管并回收) --- #### 2. **孤儿进程(Orphan Process)** - **定义**: 父进程先于子进程终止,子进程被`init`进程(PID=1)接管。 - **特征**: - 仍在运行中 - 父进程ID变为`1` - **产生原因**: 父进程意外终止(如崩溃)或主动退出但未等待子进程[^1]。 - **系统处理**: `init`进程自动回收孤儿进程,无需人工干预。 --- #### 3. **核心区别** | 特征 | 僵尸进程 | 孤儿进程 | |--------------|------------------------------|------------------------------| | **状态** | 已终止(`Z`状态) | 仍在运行 | | **父进程** | 原父进程未调用`wait()` | 原父进程终止,由`init`接管 | | **资源占用** | 少量资源泄漏 | 无长期影响 | | **解决方案** | 需父进程处理或终止父进程 | 系统自动处理 | --- #### 4. **代码示例** ```c // 产生僵尸进程的示例 #include <unistd.h> int main() { if (fork() == 0) { // 子进程立即退出 return 0; } else { // 父进程不调用wait(),睡眠30秒 sleep(30); } return 0; } ``` 运行后通过`ps aux | grep Z`可观察到僵尸进程。 --- #### 5. **实际影响** - **僵尸进程**: 长期存在会导致进程ID耗尽(极端情况下)[^2]。 - **孤儿进程**: 对系统无危害,常用于守护进程的实现[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值