SIGCHLD产生的条件:

只要子进程的状态发生变化,父进程会收到这个信号

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

// 全局变量记录子进程信息
typedef struct {
    pid_t pid;
    int index;
} child_info_t;

#define MAX_CHILDREN 5
static child_info_t children[MAX_CHILDREN];
static int child_count = 0;
static int collected_count = 0;

// SIGCHLD信号处理函数
void sigchld_handler(int sig) {
    int status;
    pid_t pid;
    int saved_errno = errno;
    
    printf("🔔 收到SIGCHLD信号\n");
    
    // 非阻塞方式回收所有已终止的子进程
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        collected_count++;
        
        // 查找是哪个子进程
        for (int i = 0; i < child_count; i++) {
            if (children[i].pid == pid) {
                if (WIFEXITED(status)) {
                    printf("✅ 回收子进程 %d (编号:%d) - 正常退出,状态=%d\n", 
                           pid, children[i].index, WEXITSTATUS(status));
                } else if (WIFSIGNALED(status)) {
                    printf("🔴 回收子进程 %d (编号:%d) - 被信号终止,信号=%d\n", 
                           pid, children[i].index, WTERMSIG(status));
                }
                break;
            }
        }
    }
    
    printf("📊 进度: 已回收 %d/%d 个子进程\n\n", collected_count, child_count);
    errno = saved_errno;
}

// 设置信号处理器
void setup_signal_handler() {
    struct sigaction sa;
    
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;  // 关键:忽略停止/继续
    
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("❌ sigaction失败");
        exit(1);
    }
    
    printf("🎯 SIGCHLD信号处理器设置成功\n");
}

// 创建子进程
pid_t create_child(int index, int sleep_time) {
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程代码
        printf("👶 子进程 %d (PID:%d) 启动,将运行 %d 秒\n", 
               index, getpid(), sleep_time);
        
        // 模拟工作
        for (int i = 0; i < sleep_time; i++) {
            printf("   子进程 %d 工作中... %d/%d\n", index, i+1, sleep_time);
            sleep(1);
        }
        
        int exit_status = 10 + index;  // 不同的退出状态
        printf("🏁 子进程 %d (PID:%d) 完成,退出状态=%d\n", 
               index, getpid(), exit_status);
        exit(exit_status);
        
    } else if (pid > 0) {
        // 父进程记录子进程信息
        if (child_count < MAX_CHILDREN) {
            children[child_count].pid = pid;
            children[child_count].index = index;
            child_count++;
        }
        return pid;
    } else {
        perror("❌ fork失败");
        return -1;
    }
}

// 显示当前进程状态
void show_process_status() {
    printf("\n📋 当前进程状态:\n");
    system("ps -o pid,ppid,state,command | grep -E '(PID|defunct|sleep|main)'");
    printf("\n");
}

int main() {
    printf("🚀 父进程启动 (PID:%d)\n", getpid());
    
    // 设置信号处理器
    setup_signal_handler();
    
    printf("\n🔄 开始创建子进程...\n");
    
    // 创建多个子进程,运行不同时间
    create_child(1, 2);  // 子进程1运行2秒
    sleep(1);
    
    create_child(2, 4);  // 子进程2运行4秒  
    sleep(1);
    
    create_child(3, 3);  // 子进程3运行3秒
    sleep(1);
    
    create_child(4, 5);  // 子进程4运行5秒
    sleep(1);
    
    create_child(5, 1);  // 子进程5运行1秒
    
    printf("\n🎯 共创建了 %d 个子进程\n", child_count);
    
    // 显示初始状态
    show_process_status();
    
    // 父进程主循环 - 模拟其他工作
    printf("👨 父进程开始处理其他任务...\n");
    for (int i = 1; i <= 10; i++) {
        printf("   父进程工作进度: %d/10\n", i);
        
        // 每2秒显示一次状态
        if (i % 2 == 0) {
            show_process_status();
        }
        
        sleep(1);
        
        // 如果所有子进程都已回收,可以提前结束
        if (collected_count >= child_count) {
            printf("🎉 所有子进程已完成回收,父进程继续收尾工作...\n");
        }
    }
    
    // 最终检查
    printf("\n📊 最终统计:\n");
    printf("   创建的子进程数: %d\n", child_count);
    printf("   回收的子进程数: %d\n", collected_count);
    
    if (collected_count == child_count) {
        printf("🎊 成功: 所有子进程都已正确回收!\n");
    } else {
        printf("⚠️  警告: 还有 %d 个子进程未回收\n", 
               child_count - collected_count);
        
        // 尝试最后的回收
        printf("🔄 尝试最终回收...\n");
        int status;
        pid_t pid;
        while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
            collected_count++;
            printf("   最终回收子进程 %d\n", pid);
        }
    }
    
    printf("🏁 父进程结束\n");
    return 0;
}

关键:

SIGCHLD信号在进程状态改变时(停止 运行 终止)都会发送信号,只需要子进程终止的时候发送信号,父进程回收

sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; // 关键:忽略停止/继续

// 设置信号处理器
void setup_signal_handler() {
    struct sigaction sa;
    
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;  // 关键:忽略停止/继续
    
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("❌ sigaction失败");
        exit(1);
    }
    
    printf("🎯 SIGCHLD信号处理器设置成功\n");
}
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐