15

If I run these commands:

dmesg | head -n 10

I presume the OS sends back some kind of signal to dmesg once head has read 10 lines. How does this work? What does head tell the kernel?

This is different from a program dying since this is a normal, 'clean' stop.

Karel
  • 1,468

3 Answers3

28

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"
cuonglm
  • 153,898
17

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.

2

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);
}
zuazo
  • 3,042
  • 1
    Highly unlikely dmesg will receive EPIPE. It's more likely dmesg will receive a SIGPIPE, and terminate. – muru Jan 18 '16 at 10:55
  • You are right, dmesg will receive SIGPIPE and will exit with an error (it does not trap that signal). – zuazo Jan 18 '16 at 11:18