Why does the server process have to open FIFO again, it just has to read again after a client process has written into it, right?
Interesting, let us try your suggestion. The following results were generated on Linux 4.9.0-6-amd64 (Ubuntu Linux kernel).
$ mkfifo t
$ (cat; cat) < t & # run a "server" as a background job
[1] 4856
$ echo 1 > t
1
[1]+ Done ( cat; cat ) < t
It didn't work as we wanted. The first cat
reads EOF as expected, and then exits. The problem is the second cat
also reads EOF immediately, and so our "server" finishes. It is not possible to wait for a new client (without repeatedly calling read() and wasting CPU time).
If you know how to manipulate file descriptors (FDs) in the shell, we can look at this another way to help confirm it.
$ echo 1 > t &
$ exec 3 < t # open "t" for reading, as FD 3.
$ cat <&3
1
[1]+ Done echo 1 > t
$ cat <&3
$
$ echo 2 > t &
[1] 5102
$ cat <&3
2
[1]+ Done echo 2 > t
$ cat <&3
$
The answer is that what re-open()
-ing the fifo achieves, is to block on someone else opening it for writing. Without that step, all subsequent calls to read()
from the fifo will immediately return 0 (EOF).
When I noticed this, I wondered how systemd-initctl
works. This program emulates the old /dev/initctl
fifo under systemd. (Disclaimer: it is not very easy to test this; I will not bother to document how). The answer is that systemd-initctl opens the fifo for both reading and writing. (Technically it is systemd which opens the fifo, as specified by systemd-initctl.socket, and passes it to systemd-initctl). Opening a fifo for reading and writing simultaneously is a Linux-specific feature. But by doing this, systemd is implementing the same trick as Stevens mentions next:
To avoid this, a useful technique
is for the daemon
to open the FIFO
two times-
once
for reading
and once
for writing.
The file descriptor
returned
for reading
is used
to
read
the client requests
,
and the
file descriptor
for writing
is never
used.
By having
the FIFO
always
open
for
writing
(as long
as the daemon
process
exists)
the reads
do not return
an EOF,
but wait
for the next
client
request.