一、什么是僵死进程(Zombie Process)?
僵死进程 :子进程先于父进程退出,但父进程没有调用 wait () /waitpid () 来回收子进程的退出状态与资源,导致子进程虽然已经停止运行,但进程描述符依然保留在系统进程表中 ,无法被彻底释放,这种状态的进程就叫 僵死进程。
简单一句话:子进程死了,尸体没人收,就变成了僵尸。
僵死进程的标志
在 ps 命令中,状态列为:
Z+
Z = Zombie(僵尸)
二、为什么会产生僵死进程?
Linux 设计规则:
- 子进程退出时,不会直接消失
- 内核会保留子进程的退出码、运行时间等信息
- 必须由父进程通过
wait()/waitpid()读取并释放 - 如果父进程不读取 → 子进程变成僵尸
三、产生僵死进程的代码示例(最经典)
下面这段代码会强制制造僵死进程:
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 那一行数字。
五、僵死进程有什么危害?
- **占用进程号(PID)**系统 PID 数量有限,大量僵尸会导致无法创建新进程。
- 占用内核内存进程描述符、task_struct 结构不会释放。
- 严重时导致系统宕机
**注意:普通 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