假设我从终端运行管道:
$ a | b
有没有办法让我从终端向两个进程发送信号,或者只能从两个进程之一读取来自终端的 stdin ?
注意我可以b
使用以下内容从标准输入中读取:
open /dev/tty
但这似乎阻止了a
从我的测试中读取标准输入。为什么?
基本上,当我发送ctrl-c或 ctrl-d到管道,并且我从 stdin 中读取时b
,a
将不会收到信号(根据我的测试)。
答案1
您需要澄清像 Ctrl-C 这样的信号不会通过 stdin 传递。当您在终端上键入 Ctrl-C 时,“Ctrl”和“C”不会作为 2 个原始字符直接传递到您的进程,tty 驱动程序会将它们解释为信号 SIGINT 并将信号发送到您的前台进程组。
如果在运行时键入 Ctrl-C
a | b
,两个进程都会收到 SIGINT 信号。要测试这一点,您可以在命令运行时键入 Ctrl-C:
tail -f /var/log/syslog | grep some_pattern
你会立即看到 shell 提示符,这意味着两个进程都中断了。
怎么样?
登录 shell 后,您将进入会议,所有在 shell 中运行的程序都属于该会话。
会话可以有一个控制终端,它通常从键盘获取输入并输出到屏幕。此会话中的进程从控制终端读取 stdin,将 stdout 和 stderr 写入控制终端,并从控制终端接收信号。
与 shell 管道相连的进程形成一个进程组。例如:
proc1 | proc2 &
或者喜欢
proc3 | proc2 | proc5
一个会话可以有一个前台进程组以及一个或多个后台进程组。阻止您的 shell 并占用您的前台屏幕的进程组是前台进程组。而运行的进程组
&
将是后台进程组。每当您在控制终端中键入“Ctrl-C”等序列时,信号将被发送到前台进程组中的所有进程。
答案2
你混合了很多东西,标准输入、输出、错误文件描述符(fds)与终端 fd 不是一回事。
当您打开终端并输入命令时,默认情况下这 3 个 fd(stdin (0)、stdout(1)、stderr(2))“指向”终端,但可以通过重定向任何这些 fd(例如“ls non_existing_file 2>err”会将错误消息重定向到文件“err”,并且您不会在终端上看到它)。
现在我想解决您的“open /dev/tty”行,它实际上指向您的终端,为了演示差异,请查看此示例程序。 简单来说一下管道的工作原理:
当您通过管道传输两个命令(没有任何“<”或“>”)时,第一个命令将从键盘获取输入(如果需要读取输入),并将其输出重定向为第二个命令的输入,即意味着进程“b”的“标准”输入不再是终端,而是进程“a”的输出。
现在要最终回答您的问题,您还需要了解两件事:
1)当您在前台启动一个进程时,shell将给予该进程对终端的控制权,并且它将等到该进程完成后再收回对终端的控制权,并且任何键盘生成的信号都将发送到该控制进程在其执行期间。
2) 当您启动管道命令 (cmd1 | cmd2) 时,shell 首先会将这 2 个进程放入同一个“进程组”中(如果您想了解更多信息,请阅读作业控制)。因此,“a | b”发生的情况是,这两个进程位于同一进程组中,因此任何键盘生成的信号都将发送到这两个进程。因为键盘生成的信号被发送到整个进程组而不是单个进程。我希望这能回答您的问题,如果有任何不清楚的地方,请随时提问