如何让信号处理函数捕获子进程发送的信号?
简易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
相关产品推荐
相关产品推荐

