In `dmesg | head`, (how) is dmesg being killed after 10 lines of output?

It depends on the OS buffers and the timing between the 10th and 11th writes of dmesg.

After head writes 10 lines, it terminates and dmesg will receive SIGPIPE signal if it continues writing to the pipe.

Depending on your OS buffer, dmesg will often write more than 10 lines before head consumes them.

To see that head had consumed more than 10 lines, you can use:

strace -f sh -c 'dmesg | head -n 10'

(Look at the head process, count on number of read system calls.)

To see how the writing speed effect:

strace -f sh -c "perl -le '$|++;print 1 while 1' | head -n 10"

Take a look at the POSIX specification for the write() function:

The write() function shall fail if:

… An attempt is made to write to a pipe or FIFO that is not open for reading by any process, or that only has one end open. A SIGPIPE signal shall also be sent to the thread.

So the sequence of events is:

  1. The head process exits. This causes all its open file descriptors to be closed, including its standard input, which is one end of the pipe.

  2. The dmesg process calls write() on its standard output, which is the other end of the pipe.

  3. This causes a SIGPIPE to be delivered to the dmesg process.

  4. dmesg has no special handling of SIGPIPE, so the default action is applied, which is to terminate the process.

You can experiment with this by changing the action for the SIGPIPE signal. For example, this pipeline terminates after printing one line:

$ yes | head -1
y
$

but if you ignore SIGPIPE, then it doesn't terminate:

$ trap '' PIPE
$ yes | head -1
y

At this point the yes process is still trying to write to the pipe but the writes are failing with EPIPE.


head closes stdin after printing 10 lines. Then dmesg detects that the stdout is closed and exits.

To be more precise, dmesg will receive the EPIPE error from the write call to stdout.

From the dmesg.c source code here: 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);
}

Tags:

Pipe

Signals