You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用kill()后子进程变为僵尸进程的原因及解决方法

kill()后子进程变僵尸进程的原因及解决方法

一、为什么会出现僵尸进程?

僵尸进程的核心原因是:子进程已经终止,但父进程没有调用wait()/waitpid()这类函数回收它的退出状态,内核为了保存子进程的退出信息,会保留其PCB(进程控制块),导致进程以<defunct>状态残留。

看你的代码,问题出在这两处:

  1. 调用kill(workProcessId, SIGTERM)终止子进程后,没有主动回收该子进程的退出状态。之前的waitpid调用是在kill前执行的,此时子进程还活着(childRes == 0表示子进程未终止),kill后子进程退出,父进程却没再调用waitpid处理它。
  2. 你直接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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 00:29:57