请详细解释(包括 tty 相关的内容)sudo
X 终端模拟器上的前台进程实际上是如何在Ctrl-C
.
请参阅以下示例:
$ sudo -u test_no_pw sleep 999 &
[1] 16657
$ ps -o comm,pid,ppid,ruid,rgid,euid,egid,suid,sgid,sid,pgid -t
COMMAND PID PPID RUID RGID EUID EGID SUID SGID SID PGID
zsh 15254 15253 1000 1000 1000 1000 1000 1000 15254 15254
sudo 16657 15254 0 1000 0 1000 0 1000 15254 16657
sleep 16658 16657 1002 1002 1002 1002 1002 1002 15254 16657
ps 16660 15254 1000 1000 1000 1000 1000 1000 15254 16660
$ fg
[1] + running sudo -u test_no_pw sleep 999
^C
$ # it was killed
在我中断之前,sudo
我strace
在另一个终端上启动了它:
# strace -p 16657
Process 16657 attached
restart_syscall(<... resuming interrupted call ...>) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL, si_value={int=809122100, ptr=0x54552036303a3934}} ---
[...SNIP...]
所以发件人很SI_KERNEL
有趣。我昨天在 IRC 频道和 Google 上询问过,但只得到了模糊或不正确的答案。大多数人说终端或 shell 会将 sudo 发送SINGINT
到 sudo 但在我看来这不会发生,根据kill(2)
:
对于有权发送信号的进程,它必须具有特权(在 Linux 下:具有 CAP_KILL 功能),或者发送进程的真实或有效用户 ID 必须等于目标的真实或保存的设置用户 ID过程。在 SIGCONT 的情况下,当发送和接收进程属于同一会话时就足够了。 (从历史上看,规则是不同的;请参阅注释。)
我预测这与将一些带有 ASCII ETX
(3) 的转义序列发送到伪终端有关,但我还远远没有理解它。 (为什么信号源自内核?)
相关但模糊/不正确:
我最感兴趣的是它在 Linux 上的工作原理。
答案1
(这是试图澄清和回答问题,但欢迎改进和纠正)。
首先,让我们从场景中删除sudo
和&
+ fg
- 因为它们不会影响电台(我假设您主要使用它们来获取 PID)。那么问题就变成了:1)运行在终端前台的进程如何接收SIGINT; 2)当终端是使用X11的伪终端(例如Xterm)时会发生什么变化。
SIGINT(以及 SIGQUIT、SIGTSTP)的传递是由内核控制终端驱动程序在截获 CTRL-C 字符时生成的,这就是您将其视为
SI_KERNEL
源的原因。无论 X11 还是伪终端,都会发生这种情况。 “Unix 环境中的高级编程第二版 (APUE2)”,图 9.7,第 272 页对此进行了很好的说明(出于版权原因,我不会将其粘贴到此处,但我确信可以找到它)。第 275 页的“9.8 作业控制”部分对此进行了进一步解释。相关的Linux内核代码大概是这样的: http://lingrok.org/xref/linux-linus/drivers/tty/n_tty.c#1254现在将伪终端添加到混合中:伪终端内核代码仍然使用标准终端代码(如上所述) - 因此当 PTY(X 终端)的“主”端接收到“的 X11 按键事件时CTRL-C”,并将其发送到从机 PTY,该字符由内核终端驱动器检测到,并将 SIGINT 发送到前台进程组(在您的情况下为 sudo )。 APUE2 第 676 页图 19.1 对此进行了说明。
在 APUE2 第 706 页中,有一个简短的段落“信号生成”提到信号可以由主 PTY 直接使用ioctl(2)
(例如http://lingrok.org/xref/linux-linus/drivers/tty/pty.c#482),但我相信这里的情况并非如此。
欢迎评论。