使用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
相关产品推荐
相关产品推荐

