调用kill()后子进程变为僵尸进程的原因及解决方法
kill()后子进程变僵尸进程的原因及解决方法
一、为什么会出现僵尸进程?
僵尸进程的核心原因是:子进程已经终止,但父进程没有调用wait()/waitpid()这类函数回收它的退出状态,内核为了保存子进程的退出信息,会保留其PCB(进程控制块),导致进程以<defunct>状态残留。
看你的代码,问题出在这两处:
- 调用
kill(workProcessId, SIGTERM)终止子进程后,没有主动回收该子进程的退出状态。之前的waitpid调用是在kill前执行的,此时子进程还活着(childRes == 0表示子进程未终止),kill后子进程退出,父进程却没再调用waitpid处理它。 - 你直接fork了新子进程并覆盖了
workProcessId,旧子进程的PID从此被父进程遗忘,再也不会被回收,最终变成僵尸。
二、彻底终止并回收子进程的两种方案
方案1:kill后主动调用waitpid回收
在发送SIGTERM信号后,立即通过waitpid轮询或阻塞等待子进程退出,确保回收其状态:
if(childRes == 0) { printf("kill process\n"); printf("kill get %d\n", kill(workProcessId, SIGTERM)); // 回收终止的子进程,避免僵尸 int ret; do { ret = waitpid(workProcessId, &status, WNOHANG); // 处理系统中断导致的waitpid失败 if (ret == -1 && errno != EINTR) { perror("waitpid failed"); break; } usleep(100000); // 短暂等待子进程完成退出 } while (ret == 0); }
方案2:注册SIGCHLD信号处理函数
父进程注册SIGCHLD信号的处理函数,当子进程终止时,内核会自动给父进程发信号,在处理函数中批量回收所有终止的子进程,一劳永逸解决僵尸问题:
// 信号处理函数:回收所有已终止的子进程 void sigchld_handler(int sig) { int status; // 用-1表示回收任意子进程,WNOHANG非阻塞 while (waitpid(-1, &status, WNOHANG) > 0); } // 在main函数初始化时注册信号处理 int main() { // 注册SIGCHLD信号处理 signal(SIGCHLD, sigchld_handler); pid_t workProcessId = fork(); if (workProcessId > 0) { parent_process_exec(workProcessId); } else { child_process_exec(); } return 0; }
三、额外注意事项
- 你的子进程在
open("./myfifo", O_WRONLY)处阻塞,收到SIGTERM后,open会被信号中断并返回错误,子进程随即退出,此时必须确保父进程能捕获到这个退出事件。 - 使用
WNOHANG的waitpid不会阻塞父进程主逻辑,适合在循环中调用,但要注意处理EINTR错误(系统中断导致的waitpid返回)。
内容的提问来源于stack exchange,提问作者Yongqi Z
相关产品推荐
相关产品推荐

