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

如何让信号处理函数捕获子进程发送的信号?

简易Shell信号捕获问题:子进程发送的SIGTSTP/SIGINT无法被父进程捕获

问题背景

用C语言实现了一个带基础作业控制的简易Shell,非内置命令会通过fork创建子进程执行。已为SIGCHLD、SIGINT、SIGTSTP注册信号处理函数,能正常捕获终端触发的信号(比如按下ctrl+z发送的SIGTSTP),但子进程通过kill(prog_pid, SIGTSTP)主动发送的信号无法触发父进程的处理函数,SIGINT也存在相同问题。

现有代码

SIGTSTP信号处理函数

void sigtstp_handler(int sig) 
{
    pid_t pid;

    if ((pid = fgpid(jobs)) > 0) { // 确认前台作业存在
        kill(-pid, SIGTSTP);
        printf("caught SIGTSTP");
    }
    
    return;
}

主函数核心逻辑

main() {
   Signal(SIGTSTP, sigtstp_handler); // 安装sigtstp_handler
   
   if ((output = builtin_cmd(argv)) == 0) {
        sigset_t mask, prev_mask;
        sigemptyset(&mask);
        sigaddset(&mask, SIGTSTP);

        // 阻塞信号
        sigprocmask(SIG_BLOCK, &mask, &prev_mask);
     
        pid = fork();
        // 子进程逻辑
        if (pid == 0) {
            // 为子进程解除信号阻塞
            sigprocmask(SIG_SETMASK, &prev_mask, NULL); 
            
            setpgid(0,0); // 设置进程组ID
                        
            // 执行用户程序
            if (execve(argv[0], argv, environ) < 0) {
                printf("Command not found\n");
                exit(0);
            } 
        }
        // 父进程逻辑
        if (pid != 0){ 
            addjob(pid);

            // 为父进程解除信号阻塞
            sigprocmask(SIG_SETMASK, &prev_mask, NULL);

            if (!background) {     
                waitfg(pid); 
            }  
        }
    }

    return;
}

问题分析与解决方法

1. 信号发送对象错误

子进程中执行kill(prog_pid, SIGTSTP)时,prog_pid是子进程自身的PID,信号只会发送给子进程自己,不会传递给父进程。若要让父进程捕获信号,子进程需明确指定父进程PID,改用kill(getppid(), SIGTSTP)。

2. 终端进程组的影响

子进程通过setpgid(0,0)成为新进程组组长后,终端前台进程组是子进程所在组,父进程属于独立进程组。终端触发的信号会默认发给前台进程组,但子进程主动发信号给父进程必须显式指定父进程ID。

3. 信号阻塞逻辑验证

父进程在fork后已解除SIGTSTP阻塞,这部分逻辑无问题。需检查waitfg函数内部是否再次阻塞了SIGTSTP/SIGINT,比如使用sigsuspend时,要确保信号集未屏蔽目标信号。

4. 处理函数的可重入性修复

原处理函数中使用的printf不是可重入函数,信号处理过程中调用可能导致IO混乱,建议替换为系统调用write:

void sigtstp_handler(int sig) 
{
    pid_t pid;

    if ((pid = fgpid(jobs)) > 0) {
        kill(-pid, SIGTSTP);
        write(STDOUT_FILENO, "caught SIGTSTP\n", 15);
    }
    
    return;
}

修复后的子进程测试示例

如果子进程执行的prog.c需要触发父进程信号处理,代码应改为:

#include <unistd.h>
#include <signal.h>

int main() {
    // 发送SIGTSTP给父进程
    kill(getppid(), SIGTSTP);
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 04:57:05