如何修改子进程的信号处理程序?Shell作业控制信号异常问题
解决子进程继承父进程信号忽略设置的问题
我一眼就看出问题所在了——你的子进程确实继承了父进程的信号忽略配置,而你当前的子进程信号代码根本没完成重置信号处理动作的工作。下面给你拆解原因和解决方案:
问题根源
当你用fork()创建子进程时,子进程会完全继承父进程的:
- 信号掩码(
sigprocmask的设置) - 所有信号的处理动作(包括父进程设为
SIG_IGN的那些信号)
你在父进程的set_signals()里把SIGINT、SIGQUIT、SIGTSTP等信号都设成了忽略,子进程fork后这些设置原封不动带过来了。更关键的是:execve()不会把SIG_IGN的信号重置为默认——它只会把自定义信号处理函数(比如你的chld_handler)重置为SIG_DFL,但已经被设为忽略的信号会保持忽略状态。
你当前的子进程代码只是初始化了sigaction结构体,却没有调用sigaction()去实际修改那些信号的处理动作,等于啥也没做。
修正后的子进程信号设置代码
你需要在子进程里显式把父进程设为忽略的信号全部改回默认(SIG_DFL),代码如下:
/* 设置为前台进程 */ pid = getpid(); if (!job->pgid) job->pgid = pid; setpgid(pid, job->pgid); tcsetpgrp(shell_terminal, job->pgid); /* 重置信号为默认处理 */ struct sigaction act; memset(&act, 0, sizeof(act)); sigfillset(&act.sa_mask); act.sa_handler = SIG_DFL; // 对应父进程忽略的信号,逐个改回默认 sigaction(SIGINT, &act, NULL); sigaction(SIGQUIT, &act, NULL); sigaction(SIGTSTP, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGTTIN, &act, NULL); sigaction(SIGTTOU, &act, NULL); // SIGCHLD可以不用改,execve会自动把自定义handler重置为默认 // 如果你需要在execve前处理SIGCHLD,也可以在这里设置,但通常不需要 execve(...);
额外提示
- 你在父进程和子进程里用
sigprocmask(SIG_SETMASK, &set, NULL)临时阻塞所有信号的操作是没问题的,但要注意后续要正确解除阻塞(你已经做了)。 - 对于作业控制的Shell,还要注意子进程的终端组设置(你已经用
setpgid和tcsetpgrp处理了,这部分是对的)。
这样修改后,子进程就会正常响应Ctrl+C(SIGINT)、Ctrl+Z(SIGTSTP)这些信号了。
内容的提问来源于stack exchange,提问作者rudi8848
相关产品推荐
相关产品推荐

