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

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

如果我运行这些命令:

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()函数的 POSIX 规范:

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

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

所以事件的顺序是:

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

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

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

  4. 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);
}

相关内容