Linux 中的僵尸进程 – 如何在 Linux 中使用已失效/僵尸进程?

在本教程中,我们将讨论一个非常有趣且内容丰富的主题,即 Linux 中的 Defunct 或 Zombie Process。 我们还将讨论 init 过程, SIGCHLD 信号,系统调用 [fork(), exit(), & wait()]和 Linux 命令 [ps, top, & kill].

目录

Linux 中的僵尸进程是什么?

在 Linux 中,一个 僵尸进程 是一个已完成执行并使用 exit() 系统调用终止的进程,但它仍然在系统的进程表中有其条目。 僵尸进程也称为 已失效的进程 因为它在进程表中仅以该名称表示。 实际上,僵尸进程就像原始僵尸一样,既不生也不死。

僵尸进程是如何创建的?

在Linux环境下,我们创建一个 子进程 使用 fork() 系统调用。 以及调用 fork() 系统调用称为 父进程.

该父进程必须在子进程终止后使用 **SIGCHLD 发出信号并立即致电 wait() 系统调用,以便它可以从系统的进程表中删除终止的子进程的条目。

这个从进程表中删除终止子进程条目的过程称为 收获. 如果父进程没有使 wait() 系统调用并继续执行其其他任务,那么它将无法在其终止时读取子进程的退出状态。

并且子进程的条目即使在其终止后仍将保留在进程表中。 因此它变成了一个僵尸进程。 默认情况下,每个子进程都是僵尸进程,直到其父进程等待读取其退出状态,然后从进程表中获取其条目。

**SIGCHLD 信号:当子进程发生有趣的事情,比如停止或终止时,会向子进程的父进程发送一个 SIGCHLD 信号,以便它可以读取子进程的退出状态。 默认情况下,对该 SIGCHLD 信号的响应是忽略它。

用于创建僵尸进程的 C 代码。

#include <stdio.h>
#include <stdlib.h> // for exit()
#include <unistd.h> // for fork(), and sleep()

int main()
{
    // Creating a Child Process
    int pid = fork();
  
    if (pid > 0)         // True for Parent Process 
        sleep(60);                  
    else if (pid == 0)   // True for Child Process
        {
            printf("Zombie Process Created Successfully!");
            exit(0);
        }
    else                 // True when Child Process creation fails
        printf("Sorry! Child Process cannot be created...");
  
    return 0;
}

输出:

该程序创建了一个僵尸进程,因为子进程使用 exit() 在父进程处于睡眠状态并且不等待其子进程读取其退出状态时进行系统调用。 这个 Zombie 进程将只存在 60 秒,然后父进程将被终止,然后 Zombie 进程将被自动杀死。 我们可以在系统中看到这个僵尸进程 [output] <已失效> 使用以下以红色突出显示的 Linux 命令。

ubuntu:~$ ps -ef

输出:

ps -ef 命令的输出

我们还可以使用以红色突出显示的 top 命令定位此 Zombie 进程的进程表条目。

ubuntu:~$ top

输出:

已停止进程僵尸进程的 top 命令输出top 命令的输出

僵尸进程与孤儿进程

僵尸进程不应与孤立进程混淆。 因为孤儿进程是一个即使在其父进程终止后仍保持非活动或运行状态的进程,而僵尸进程仍保持非活动状态,所以它只在系统的进程表中保留其条目。

孤儿进程可以有两种类型:

  1. 故意孤立的进程: 有意孤儿进程是当我们必须启动/运行无限运行的服务或完成不需要任何用户干预的长时间运行的任务时生成的孤儿进程。 这些进程在后台运行,通常不需要任何手动支持。
  2. 无意中孤立的进程: 无意孤儿进程是当某些父进程崩溃或终止使其子进程处于活动或运行状态时生成的孤儿进程。 与 Intentionally Orphaned 进程不同,这些进程可以由用户使用进程组机制来控制或避免。

僵尸进程的特征

以下是僵尸进程的一些特征:

  • Zombie 进程的退出状态可以由捕获 SIGCHLD 信号的父进程使用 wait() 系统调用。
  • 当父进程读取僵尸进程的退出状态时,它的条目将从进程表中获取。
  • 从进程表中收获一个僵尸进程后,它的PID(进程ID)和进程表条目可以被系统中的一些新进程重用。
  • 如果 Zombie 进程的父进程终止或完成,则进程表中 Zombies 进程条目的存在会产生操作系统故障。
  • 通常,僵尸进程可以通过使用 SIGCHLD 信号发送到父进程来销毁 kill 命令。
  • 如果即使通过向其父进程发送 SIGCHLD 信号也无法销毁 Zombie 进程,那么我们可以终止其父进程以杀死 Zombie 进程。
  • 随着 Zombie 的父进程终止或完成,Zombie 进程被 init 进程,然后通过捕获 SIGCHLD 信号并读取其退出状态来杀死僵尸进程,因为它一直在执行 wait() 系统调用。

与僵尸进程相关的威胁

虽然僵尸进程不使用任何系统资源,但在系统的进程表中保留其条目(PID)。 但值得关注的是系统进程表的大小有限。 每个活动进程在系统进程表中都有一个有效条目。

无论如何,如果创建了非常大量的 Zombie 进程,那么每个 Zombie 进程将占用一个 PID 和系统进程表中的一个条目,并且进程表中将没有空间。

这样,系统中大量Zombie进程的存在可以阻止任何新进程的产生,并且系统会因为没有任何PID(进程ID)可用,也没有进程中的空间而进入不一致状态桌子。

此外,僵尸进程的存在会在其父进程不活动时产生操作系统故障。

如果系统中只有几个 Zombie 进程,这不是问题,但当系统中有这么多 Zombie 进程时,这可能会成为系统的严重问题。

系统中的最大僵尸进程数

我们可以使用以下 C 程序找到系统中僵尸进程的最大数量。 这个 C 程序在 Linux 环境中执行时,它会尝试使用 fork() 内部调用的系统调用 while 环形。

我们知道 fork() 系统调用只有在创建子进程失败时才会返回负值,所以 while 只要成功创建子进程并且循环条件为真 flag 柜台里面 while 循环将增加。

我们还讨论过系统中进程表的大小是有限的,因此在创建大量子进程(僵尸进程)后,进程表中将没有空间,然后 fork() 系统调用将无法再创建任何子进程,因此返回负值,这使得 while 循环条件为假。

这样, while 循环将停止,最后 flag 计数器值表示僵尸进程的总数。

#include<stdio.h>
#include<unistd.h> // for fork()

int main()
{

    int flag = 0; // Counter variable
    while (fork() > 0)
    {
                flag++;  // Counts the No. of Zombie Processes
                printf("%dn", flag);
    }
    return 0;
}

输出:

输出僵尸进程总数僵尸进程总数

从输出中,我们可以清楚地看到系统内部可以创建的最大僵尸进程数是 24679.

笔记: Zombie Processes 的最大数量是 flag 每次运行上述 C 程序时,计数器都会有所不同,具体取决于系统进程表中的空闲空间。

如何杀死僵尸进程?

如果父进程不等待其子进程的终止,它将无法捕获 SIGCHLD 信号,因此不会读取子进程的退出状态。 然后它的条目保留在进程表中,它变成了一个僵尸进程。

然后我们必须通过向父进程发送 SIGCHLD 信号来销毁这个僵尸进程,使用 kill Linux 中的命令。

当父进程收到 SIGCHLD 信号时,它通过使用 wait() 系统调用。 以下是 Linux 手动杀死 Zombie 进程的命令演示:

ubuntu:~$ kill -s SIGCHLD <PID>

如果无论如何即使通过向父进程发送 SIGCHLD 信号也无法销毁 Zombie 进程,那么我们可以终止其父进程然后

如果即使通过向父进程发送 SIGCHLD 信号也无法销毁 Zombie 进程,那么我们可以终止其父进程,然后 Zombie 进程将被 在里面 进程(PID = 1)。

init 进程现在成为僵尸进程的新父进程,它定期使 wait() 系统调用捕获 SIGCHLD 信号以读取 Zombie 进程的退出状态并从进程表中获取它。 以下是杀死父进程的 Linux 命令:

ubuntu:~$ kill -9 <PID>

笔记: 在上述两个 Linux 命令中,只需将 替换为 Zombie 父进程的 PID(进程 ID)即可。

加起来

在本教程中,我们了解了 Linux 中的 Zombie Process 或 Defunct Process,它是如何在系统内部创建的,如何在系统中定位 Zombie Process,Zombie Process 和 Orphan Process 的区别,Zombie 的各种特征进程,与Zombie Process相关的威胁,如何找到系统中Zombie Process的最大数量,以及杀死系统中Zombie Process的不同方法。