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

使用sigaction处理SIGCHLD时父进程为何提前退出?

父进程在子进程退出前提前退出的原因及解决方法

问题描述

我正在学习如何通过sigaction结合SIGCHLD信号处理僵尸进程。预期行为是:子进程根据随机数休眠5-30秒后退出,父进程休眠30秒后再退出。但实际情况是父进程在子进程退出前就提前退出了,这是什么原因?

附上测试代码:

#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

sig_atomic_t child_exit_status;

void clean_up_child_process(int signal_number){
  int status;
  wait(&status);
  child_exit_status = status;
}

int main(){
  struct sigaction sigchld_action;
  int random_number;
  pid_t child_pid;

  memset(&sigchld_action, 0, sizeof(sigchld_action));
  sigchld_action.sa_handler = &clean_up_child_process;
  sigaction(SIGCHLD, &sigchld_action, NULL);

  for (int i = 0; i < 5; i++) {

    child_pid = fork();

    if (child_pid < 0) {
        perror("fork");
        exit(1);
    }

    if (child_pid == 0) {
        // Child process
        printf("Child %d created\n", i);

        srand(time(NULL)^getpid());
        random_number = (rand()%25)+5;;
        printf("random number is %d\n", random_number);

        sleep(random_number); // Simulate some work in the child
        exit(0);
    }
  }

  sleep(30);

  return 0;
}

核心原因

父进程提前退出的关键在于**sleep()是可中断的系统调用**:

  • 当子进程退出时,内核会向父进程发送SIGCHLD信号,此时父进程正在执行sleep(30),内核会唤醒父进程并终止sleep调用,使其提前返回剩余的睡眠时间(而非等待满30秒)。
  • 父进程从sleep返回后,直接执行return 0退出,因此出现“提前退出”的现象。

你可以在sleep(30)后添加一行代码验证:

int ret = sleep(30);
printf("sleep返回剩余时间:%d秒\n", ret);

运行后会发现ret不为0,说明sleep被信号打断了。

解决方法

根据你的需求,有两种常见的处理方式:

方式1:确保父进程休眠满30秒

通过循环调用sleep,直到它返回0(即休眠时间完全用完):

int remaining = 30;
while ((remaining = sleep(remaining)) > 0) {
    // 被信号打断后,继续休眠剩余的时间
}

方式2:等待所有子进程退出后再结束父进程

如果你的真实需求是父进程等待所有子进程完成后再退出,而非固定休眠30秒,可以修改信号处理函数,用waitpid循环处理所有退出的子进程(避免信号丢失导致僵尸进程残留),同时在主进程中等待所有子进程结束:

修改信号处理函数:

void clean_up_child_process(int signal_number){
    int status;
    // 使用waitpid(-1, ..., WNOHANG)循环处理所有已退出的子进程
    while (waitpid(-1, &status, WNOHANG) > 0) {
        child_exit_status = status;
        // 可以在这里打印子进程退出信息
    }
}

主进程中替换sleep(30)为等待所有子进程:

// 等待所有子进程退出
pid_t pid;
while ((pid = waitpid(-1, NULL, WNOHANG)) != -1 || errno != ECHILD) {
    // 空循环,直到没有子进程
}

额外注意点

  • 原代码中使用wait()处理SIGCHLD信号存在风险:当多个子进程几乎同时退出时,SIGCHLD信号会被合并,一次信号处理只能回收一个子进程,剩余的子进程会变成僵尸进程。改用waitpid(-1, &status, WNOHANG)循环处理可以避免这个问题。
  • 子进程中srand(time(NULL)^getpid())的写法是合理的,通过异或getpid()避免多个子进程在同一秒启动时生成相同的随机数序列。

内容的提问来源于stack exchange,提问作者idiot one

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:17:49