如果我运行这些命令:
dmesg | head -n 10
我认为操作系统会在读取 10 行dmesg
后发送回某种信号。head
这是如何运作的?head
告诉内核什么?
这与程序终止不同,因为这是正常的“干净”停止。
答案1
它取决于操作系统缓冲区以及第 10 次和第 11 次写入之间的时序dmesg
。
head
写入10行后,它终止并dmesg
会收到SIGPIPE信号如果它继续写入管道。
根据您的操作系统缓冲区,通常会在消耗它们dmesg
之前写入 10 多行。head
要查看已head
消耗超过 10 行,您可以使用:
strace -f sh -c 'dmesg | head -n 10'
(查看head
进程,统计read
系统调用次数。)
查看写入速度的影响:
strace -f sh -c "perl -le '$|++;print 1 while 1' | head -n 10"
答案2
如果出现以下情况,该
write()
功能将失败:… 尝试写入未打开供任何进程读取或仅一端打开的管道或 FIFO。 SIGPIPE 信号也应发送到线程。
所以事件的顺序是:
进程
head
退出。这会导致其所有打开的文件描述符被关闭,包括其标准输入(管道的一端)。该
dmesg
进程调用write()
其标准输出,即管道的另一端。这会导致 SIGPIPE 被传递到进程
dmesg
。dmesg
对 SIGPIPE 没有特殊处理,因此应用默认操作,即终止进程。
您可以通过更改 SIGPIPE 信号的操作来进行试验。例如,该管道在打印一行后终止:
$ yes | head -1
y
$
但如果你忽略 SIGPIPE,那么它不会终止:
$ trap '' PIPE
$ yes | head -1
y
此时,yes
进程仍在尝试写入管道,但写入失败并出现 EPIPE。
答案3
head
关闭标准输入打印10行后。然后dmesg
检测到标准输出关闭并退出。
更准确地说,将从调用中dmesg
收到EPIPE
错误write
标准输出。
来自dmesg.c源代码在这里:https://github.com/karelzak/util-linux/blob/v2.27.1/sys-utils/dmesg.c#L654-L659
rc = fwrite(p, 1, len, out) != len;
if (rc != 0) {
if (errno != EPIPE)
err(EXIT_FAILURE, _("write failed"));
exit(EXIT_SUCCESS);
}