在`dmesg | head`,(如何)在 10 行输出后 dmesg 被杀死?

Kar*_*rlo 15 pipe signals

如果我运行这些命令:

dmesg | head -n 10
Run Code Online (Sandbox Code Playgroud)

我认为操作系统发送回某种信号,dmesg一旦head读取了 10 行。这是如何运作的?这是什么head告诉内核?

这与程序终止不同,因为这是正常的“干净”停止。

cuo*_*glm 28

这取决于操作系统缓冲区和 .10 次和 11 次写入之间的时间dmesg

head写10行,它终止,dmesg会收到SIGPIPE信号,如果继续写入管道。

根据您的操作系统缓冲区,dmesg在使用head它们之前通常会写入 10 多行。

要查看head已消耗超过 10 行的内容,您可以使用:

strace -f sh -c 'dmesg | head -n 10'
Run Code Online (Sandbox Code Playgroud)

(看head进程,数read系统调用数。)

查看写入速度效果如何:

strace -f sh -c "perl -le '$|++;print 1 while 1' | head -n 10"
Run Code Online (Sandbox Code Playgroud)


小智 17

看看函数POSIX 规范write()

write()如果出现以下情况,该功能将失败:

... 尝试写入未打开供任何进程读取或仅打开一端的管道或 FIFO。SIGPIPE 信号也应发送到线程。

所以事件的顺序是:

  1. head过程出来。这会导致其所有打开的文件描述符都被关闭,包括其标准输入,即管道的一端。

  2. dmesg进程调用write()其标准输出,即管道的另一端。

  3. 这会导致 SIGPIPE 被传送到dmesg进程。

  4. dmesg 对 SIGPIPE 没有特殊处理,因此应用默认操作,即终止进程。

您可以通过更改 SIGPIPE 信号的操作来对此进行试验。例如,此管道在打印一行后终止:

$ yes | head -1
y
$
Run Code Online (Sandbox Code Playgroud)

但如果您忽略 SIGPIPE,则它不会终止:

$ trap '' PIPE
$ yes | head -1
y
Run Code Online (Sandbox Code Playgroud)

此时,yes进程仍在尝试写入管道,但 EPIPE 写入失败。