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

为何已调用signal()注册SIGUSR1处理函数,子进程仍被终止?

问题:已注册SIGUSR1信号处理函数,子进程为何仍被该信号终止?

我写了一段C代码,期望实现父进程发送SIGUSR1信号给子进程,子进程捕获信号并输出指定内容:

void sig_handler(int sig) { printf("Hello child\n"); }
int main() {
    pid_t child = fork();
    if (child > 0) {
        printf("Hello parent\n");
        kill(child, SIGUSR1);
    } else if (child == 0) {
        signal(SIGUSR1, sig_handler);
        printf("In child\n");
    } else {
        printf("Error\n");
    }
    return 0;
}

期望输出:

Hello parent
Hello child
In child

但实际运行后,父进程调用kill(child, SIGUSR1)后,子进程直接被终止,实际输出只有:

Hello parent

我知道SIGUSR1的默认行为是终止进程,但明明已经通过signal(SIGUSR1, sig_handler)注册了信号捕获处理函数,为什么子进程还是被终止了?


解答

核心问题出在进程执行的时序不确定性上:

当你调用fork()创建子进程后,父进程和子进程的执行顺序完全由操作系统调度器决定,没有固定的先后顺序。在你的代码里,很大概率是父进程先完成了kill(child, SIGUSR1)的调用,而此时子进程还没来得及执行到signal(SIGUSR1, sig_handler)这一行——也就是说,子进程还没为SIGUSR1注册自定义处理函数,信号的默认终止行为就被触发了,自然看不到后续的输出。

如何解决这个问题?

你需要确保子进程完全注册好信号处理函数之后,父进程再发送信号。这里提供几种常见的方案:

1. 简单延迟(适合测试场景)

在父进程的kill调用前加入短暂睡眠,给子进程足够的时间完成信号注册:

void sig_handler(int sig) { printf("Hello child\n"); }
int main() {
    pid_t child = fork();
    if (child > 0) {
        printf("Hello parent\n");
        sleep(1); // 等待子进程完成信号注册
        kill(child, SIGUSR1);
        wait(NULL); // 等待子进程执行完毕
    } else if (child == 0) {
        signal(SIGUSR1, sig_handler);
        printf("In child\n");
        pause(); // 暂停等待信号,避免子进程提前退出
    } else {
        printf("Error\n");
    }
    return 0;
}

注意:子进程需加入pause(),否则注册完信号后会直接执行return 0退出,可能父进程发信号时子进程已不存在。

2. 可靠进程同步(适合生产场景)

睡眠的方式依赖系统调度,不够可靠。更严谨的做法是让子进程在注册好信号后主动通知父进程,父进程收到通知后再发送SIGUSR1,比如用另一个信号同步:

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

void sig_handler(int sig) { 
    if (sig == SIGUSR1) {
        printf("Hello child\n");
    }
}

// 父进程接收子进程就绪信号的空处理函数
void ready_handler(int sig) {}

int main() {
    // 父进程先注册子进程就绪的信号处理函数
    signal(SIGUSR2, ready_handler);

    pid_t child = fork();
    if (child > 0) {
        printf("Hello parent\n");
        pause(); // 等待子进程发送就绪信号
        kill(child, SIGUSR1); // 子进程已就绪,发送目标信号
        wait(NULL); // 等待子进程退出
    } else if (child == 0) {
        // 注册SIGUSR1处理函数
        signal(SIGUSR1, sig_handler);
        printf("In child\n");
        kill(getppid(), SIGUSR2); // 通知父进程:已准备好接收信号
        pause(); // 等待目标信号
    } else {
        printf("Error\n");
    }
    return 0;
}

这种双向信号同步的方式,完全不受调度器影响,能确保时序的正确性。

3. 用sigaction替代signal(可选但推荐)

signal函数的行为在不同系统中存在兼容性差异,而sigaction是POSIX标准的信号处理接口,行为更稳定可靠。替换后的注册代码示例:

struct sigaction sa;
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask); // 清空信号掩码
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);

内容的提问来源于stack exchange,提问作者Eager-to-learn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 12:02:37