rob*_*b87 77 shell process signals
给定一个 shell 进程(例如sh)及其子进程(例如cat),如何使用 shell 的进程 ID模拟Ctrl+的行为C?
这是我尝试过的:
运行sh然后cat:
[user@host ~]$ sh
sh-4.3$ cat
test
test
Run Code Online (Sandbox Code Playgroud)
发送SIGINT到cat从另一终端:
[user@host ~]$ kill -SIGINT $PID_OF_CAT
Run Code Online (Sandbox Code Playgroud)
cat 收到信号并终止(如预期)。
向父进程发送信号似乎不起作用。为什么信号cat在发送到其父进程时没有传播到sh?
这不起作用:
[user@host ~]$ kill -SIGINT $PID_OF_SH
Run Code Online (Sandbox Code Playgroud)
phe*_*mer 108
第一件事是了解CTRL+是如何C工作的。
当您按下CTRL+ 时C,您的终端模拟器会发送一个 ETX 字符(文本结束/0x03)。
TTY 被配置为当它接收到这个字符时,它会向终端的前台进程组发送一个 SIGINT。可以通过执行stty -a和查看来查看此配置intr = ^C;。的POSIX说明书说,当接收时INTR,它应发送一个SIGINT给该终端的前台进程组。
那么,现在的问题是,如何确定前台进程组是什么?前台进程组只是将接收键盘生成的任何信号(SIGTSTP、SIGINT 等)的进程组。
确定进程组 ID 的最简单方法是使用ps:
ps ax -O tpgid
Run Code Online (Sandbox Code Playgroud)
第二列将是进程组 ID。
现在我们知道进程组 ID 是什么,我们需要模拟向整个组发送信号的 POSIX 行为。
这可以kill通过-在组 ID 前面放置一个来完成。
例如,如果您的进程组 ID 是 1234,您将使用:
kill -INT -1234
Run Code Online (Sandbox Code Playgroud)
所以上面涵盖了如何模拟CTRL+C作为手动过程。但是,如果您知道 TTY 号码,并且想为该终端模拟CTRL+C呢?
这变得非常容易。
让我们假设$tty是您想要定位的终端(您可以通过tty | sed 's#^/dev/##'在终端中运行来获得它)。
kill -INT -$(ps h -t $tty -o tpgid | uniq)
Run Code Online (Sandbox Code Playgroud)
这将向任何前台进程组发送 SIGINT $tty。
G-M*_*ca' 18
正如vinc17 所说,这种情况没有发生。当您键入一个信号生成键序列(例如,Ctrl+ C)时,该信号将发送到所有附加到(关联)到终端的进程。由 生成的信号没有这样的机制kill。
但是,像这样的命令
kill -SIGINT -12345
Run Code Online (Sandbox Code Playgroud)
将信号发送给进程组12345 中的所有进程;请参阅kill(1) 和kill(2)。shell 的子进程通常在 shell 的进程组中(至少,如果它们不是异步的),因此将信号发送到 shell 的 PID 的负数可能会做你想要的。
正如vinc17 指出的那样,这不适用于交互式 shell。这是一个可能有效的替代方法:
kill -SIGINT -$(echo $(ps -p PID_of_shell o tpgid=))
ps -pPID_of_shell获取有关 shell 的进程信息。
o tpgid=告诉ps只输出终端进程组 ID,没有标题。如果小于 10000,ps将显示前导空格;这$(echo …)是去除前导(和尾随)空格的快速技巧。
我确实在 Debian 机器上进行了粗略测试。
小智 12
这个问题有它自己的答案。将 发送SIGINT到cat进程kill是对按下Ctrl+时发生的情况的完美模拟C。
更准确地说,中断字符(^C默认情况下)发送SIGINT到终端前台进程组中的每个进程。如果不是cat运行涉及多个进程的更复杂的命令,则必须终止进程组才能达到与^C.
当您在没有&后台操作员的情况下运行任何外部命令时,shell 会为该命令创建一个新的进程组,并通知终端该进程组现在处于前台。shell 仍然在它自己的进程组中,它不再处于前台。然后 shell 等待命令退出。
这就是您似乎因一个常见的误解而成为受害者的地方:认为 shell 正在做一些事情来促进其子进程和终端之间的交互。那不是真的。一旦完成设置工作(进程创建、终端模式设置、管道创建和其他文件描述符的重定向,以及执行目标程序),shell 就会等待。您输入的cat内容不会通过外壳,无论是正常输入还是生成信号的特殊字符,如^C. 该cat工艺具有通过自身的文件描述符直接访问终端,终端有直接将信号发送到能力cat的过程,因为它是前台进程组。
在后cat工序模具,外壳将被通知,因为它的父cat进程。然后外壳变为活动状态并再次将自己置于前台。
这里有一个练习来增加你的理解。
在新终端的 shell 提示符下,运行以下命令:
exec cat
Run Code Online (Sandbox Code Playgroud)
该exec关键字使外壳程序在cat不创建子进程的情况下执行。外壳由cat. 以前属于 shell 的 PID 现在是cat. ps在不同的终端中验证这一点。输入一些随机的行并查看cat它们是否重复给您,证明尽管没有作为父进程的 shell 进程,它仍然表现正常。现在按Ctrl+会发生什么C?
回答:
SIGINT 被传送到 cat 进程,该进程死亡。因为它是终端上的唯一进程,所以会话结束,就像您在 shell 提示符下说“退出”一样。实际上,猫曾经是您的外壳。