A hopefully more systematic explanation:
- A pipe is an instance managed by the operating system. It has a single read end and a single write end.
- Both ends can be opened by multiple processes. There is still only one pipe, though. That is, multiple processes can share the same pipe.
- A process that has opened one of the ends holds a corresponding file handle. The process can actively
close()
it again! If a process exits, the operating system closes the corresponding file handle for you.
- All involved processes can
close()
their file handle representing the read end of the pipe. Nothing wrong with that, this is a perfectly fine situation.
- Now, if a process writes data to the write end of the pipe and the read end is not opened anymore (no process holds an open file handle for the read end), a POSIX-compliant operating system sends a
SIGPIPE
signal to the writing process for it to know that there is no reader anymore.
This is the standard mechanism by which the receiving program can implicitly tell the sending program that it has stopped reading. Have you ever wondered if
cat bigfile | head -n5
actually reads the entire bigfile? No, it does not, because cat
retrieves a SIGPIPE
signal as soon as head
exits (after reading 5 lines from stdin). The important thing to appreciate: cat
has been designed to actually respond to SIGPIPE
(that is an important engineering decision ;)): it stops reading the file and exits. Other programs are designed to ignore SIGPIPE
(on purpose, these handle this situation on their own -- this is common in networking applications).
If you keep the read end of the pipe open in your controlling process, you disable described mechanism. dmesg
will not be able to notice that grep
has exited.
However, your example actually is not a good one. grep hda
will read the entire input. dmesg
is the process that exits first.