TTY中断为何由PID 0发送?如何自定义Shell信号快捷键?
终端信号相关问题解析
首先附上用于捕获信号的代码:
#include <stdio.h> #include <unistd.h> #include <string.h> #include <signal.h> static void my_handler(int signum, siginfo_t *siginfo, void *context) { FILE *f = fopen("sig_clog.txt", "a"); fprintf(f, "Got signal '%d' from process '%d' of user '%d'\n", signum, siginfo->si_pid, siginfo->si_uid); fclose(f); } int main(void) { FILE *f = fopen("sig_clog.txt", "w"); fclose(f); struct sigaction act; memset(&act, '\0', sizeof(act)); act.sa_sigaction = &my_handler; act.sa_flags = SA_SIGINFO; sigaction(SIGINT, &act, NULL); sigaction(SIGTSTP, &act, NULL); sigaction(SIGCONT, &act, NULL); sigaction(SIGHUP, &act, NULL); printf("Hi, my pid is %d\n", getpid()); printf("my parent pid is %d\n", getppid()); while(1) sleep(1000); return 0; }
一、为什么Ctrl+C触发的信号PID/UID都是0?
按下Ctrl+C时,信号是内核直接发送的,而非Shell进程。终端驱动(内核的一部分)检测到中断组合键后,会向前台进程组的所有进程发送SIGINT信号。由于内核不是用户态进程,没有有效的PID和UID,所以siginfo结构体里的si_pid和si_uid会被设为0,以此标识信号来自内核。
而用kill命令发信号时,是Shell(或kill子进程)作为用户态进程调用kill()系统调用来请求内核发信号,这时候内核会把发送方的真实PID和UID填充到siginfo里,所以日志能显示正确的进程信息。
二、Shell如何让内核发送信号?
Shell本身不直接发送信号,而是通过系统调用或配置终端属性让内核执行发送动作:
- 执行
kill <pid>时,Shell会启动kill子进程,该进程调用kill()系统调用,内核收到请求后向目标进程发信号,并记录kill进程的PID/UID。 - 对于Ctrl+C、Ctrl+Z这类快捷键,Shell只是配置终端属性,告诉终端驱动:当检测到特定组合键时,向前台进程组发送对应信号。实际的信号发送动作由内核终端驱动完成,因此发送方标记为内核(PID 0)。
三、能否自定义Shell快捷键发送其他信号?
可以,不同Shell配置方式略有不同,以Bash为例:
1. 用stty修改终端默认快捷键
终端快捷键由设备控制属性决定,通过stty可查看或修改:
- 查看当前配置:
stty -a,会列出所有控制键对应的动作,比如intr = ^C表示Ctrl+C对应SIGINT。 - 注意:终端默认只支持少数信号的快捷键映射(如SIGINT、SIGTSTP),像SIGKILL这类信号没有默认的终端动作,无法直接通过
stty绑定让内核发送。
2. 用Bash的bind命令自定义按键绑定
如果要让Ctrl+K发送SIGKILL给前台进程,可添加如下绑定:
bind '"\C-k": "\C-u kill -KILL %% \C-m"'
\C-k代表Ctrl+K;\C-u清空当前行(可选);%%是Bash特殊语法,指代当前前台进程;\C-m模拟回车,自动执行命令。
这种方式是通过Shell执行kill命令发信号,日志里会记录Shell或kill进程的PID,而非内核的0。
内容的提问来源于stack exchange,提问作者Sourav Kannantha B
相关产品推荐
相关产品推荐

