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:重启系统(不推荐)

相关推荐
Thneonl29 分钟前
全集群钟差 300 毫秒会发生什么:证书悄悄过期,日志倒流
运维·后端
是jin奥34 分钟前
Ubuntu 访问 Windows 共享目录
linux·运维·ubuntu
Gauss松鼠会35 分钟前
【GaussDB】破除gaussdb ugin索引支持中文模糊查询的迷思-字符序
java·运维·服务器·网络·数据库·gaussdb·经验总结
科技象限38 分钟前
数据防泄密系统怎么选?看它怎么把几道防线叠成网
运维·安全·网络安全·安企神
Mr_韩1 小时前
子网路由实战:无需公网 IP,异地直接访问家庭内网全部设备
运维·网络·物联网·nas
Bruce_Liuxiaowei1 小时前
内网“影子资产“:串口服务器安全盲区剖析
服务器·网络·安全·iot
星恒随风1 小时前
Linux基础IO万字详解:从文件描述符fd到重定向、FILE缓冲机制及MiniShell升级
linux·运维·笔记·学习·状态模式
yunwei371 小时前
eBPF 入门开发实践教程一:Hello World,基本框架和开发流程
linux·后端·性能优化
小雪崩1 小时前
嵌入式学习 day65:Linux驱动进阶——pinctrl 子系统和 platform 驱动框架
linux·学习·驱动
亚川楼宇自控系统数据中心厂家1 小时前
隧道智能照明:如何实现按需调光、节能又安全
运维