Linux 03:僵死进程(Zombie Process)原理、危害与解决方案

一、什么是僵死进程(Zombie Process)?

僵死进程 :子进程先于父进程退出,但父进程没有调用 wait () /waitpid () 来回收子进程的退出状态与资源,导致子进程虽然已经停止运行,但进程描述符依然保留在系统进程表中 ,无法被彻底释放,这种状态的进程就叫 僵死进程

简单一句话:子进程死了,尸体没人收,就变成了僵尸。

僵死进程的标志

ps 命令中,状态列为:

复制代码
Z+

Z = Zombie(僵尸)


二、为什么会产生僵死进程?

Linux 设计规则:

  1. 子进程退出时,不会直接消失
  2. 内核会保留子进程的退出码、运行时间等信息
  3. 必须由父进程通过 wait() / waitpid() 读取并释放
  4. 如果父进程不读取 → 子进程变成僵尸

三、产生僵死进程的代码示例(最经典)

下面这段代码会强制制造僵死进程

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
    pid_t pid = fork(); // 创建子进程

    if (pid == 0) {
        // 子进程
        printf("子进程 PID = %d 开始运行\n", getpid());
        printf("子进程退出,即将变成僵尸...\n");
        return 0; // 子进程立刻退出
    }
    else {
        // 父进程
        printf("父进程 PID = %d\n", getpid());
        while (1) { // 父进程死循环,不回收子进程
            sleep(1);
        }
    }
}

运行结果

复制代码
父进程 PID = 1234
子进程 PID = 1235 开始运行
子进程退出,即将变成僵尸...

此时子进程已经退出,但父进程死循环不回收 → 子进程变成僵死进程


四、如何查看僵死进程?

方法 1:ps 命令

bash 复制代码
ps aux | grep Z

看到 Z+ 就是僵尸进程。

方法 2:查看系统僵尸总数

bash 复制代码
top

查看 Zombie 那一行数字。


五、僵死进程有什么危害?

  1. **占用进程号(PID)**系统 PID 数量有限,大量僵尸会导致无法创建新进程。
  2. 占用内核内存进程描述符、task_struct 结构不会释放。
  3. 严重时导致系统宕机

**注意:普通 kill 命令杀不死僵尸进程!**因为它已经 "死了",只是尸体没清理。


六、如何解决 / 避免僵死进程?(3 种最常用方案)

方案 1:父进程调用 wait () 阻塞等待回收(最简单)

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();

    if (pid == 0) {
        printf("子进程 %d 运行并退出\n", getpid());
        return 0;
    } else {
        wait(NULL); // 父进程阻塞等待子进程退出并回收
        printf("父进程成功回收子进程!\n");
        while(1) sleep(1);
    }
}

无僵尸进程


方案 2:使用 waitpid () 非阻塞回收(推荐)

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();

    if (pid == 0) {
        printf("子进程 %d 退出\n", getpid());
        return 0;
    } else {
        // 非阻塞轮询回收
        while (waitpid(pid, NULL, WNOHANG) == 0) {
            printf("父进程工作中...\n");
            sleep(1);
        }
        printf("子进程已回收\n");
    }
    return 0;
}

方案 3:用信号机制 SIGCHLD 异步回收(企业级)

子进程退出时,内核会向父进程发送 SIGCHLD 信号

父进程捕获信号,在信号处理函数中回收。

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

// 信号处理函数:回收僵尸
void handler(int sig) {
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main() {
    signal(SIGCHLD, handler); // 注册信号

    pid_t pid = fork();
    if (pid == 0) {
        printf("子进程 %d 退出\n", getpid());
        return 0;
    } else {
        while (1) {
            printf("父进程运行中...\n");
            sleep(1);
        }
    }
}

完全无阻塞、无僵尸、企业标准写法


七、已经产生的僵尸进程怎么杀?

两种方法:

方法 1:杀死父进程

父进程死掉后,僵尸子进程会被 init 进程(pid=1) 收养并自动回收。

bash 复制代码
kill -9 父进程PID

方法 2:重启系统(不推荐)

相关推荐
MAVER1CK1 小时前
Docker容器创建好后修改容器配置
运维·docker·容器
匆匆那年9673 小时前
VSCode 远程 Linux 使用Codex
linux·ide·vscode
それども3 小时前
Gradle 构建疑难杂症 Could not find netty-transport-native-epoll-linux-aarch_64.ja
java·服务器·gradle·maven
NightReader4 小时前
CPU 高使用率,怎么降下来
运维·服务器
SWAGGY..4 小时前
Linux系统编程:(七)Makefile入门:轻松掌握编译自动化
linux·运维·自动化
开开心心就好5 小时前
免费流畅的远程控制实用工具
linux·运维·服务器·网络·智能手机·excel
黑猫学长呀6 小时前
存储宝典第2篇:盲封TT wafer是什么意思?
linux·嵌入式硬件·项目·芯片·ufs·晶圆·产测
Strugglingler6 小时前
【Linux 用户态操作 UART】
linux·uart
代码熬夜敲Q7 小时前
ENSP 网络工程实验
linux·运维·服务器
銳昊城7 小时前
项目七: 配置与管理Web服务器(2) C2
运维·服务器