0

While studying from Richard Stevens' book Unix network programming, I came across the following lines that speak about using FIFO between a client and a server.

Client processes are started and they open the FIFO for writing ,write their request, and exit. What happens is the read returns zero to the daemon every time a client process terminates. the daemon then has to open the FIFO again(for read only) and it waits here until a client process opens it for writing.

I did not understand the last line. 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?

0

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 use­ful 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.

  • I am sorry, being a beginner I didnt quite understand the significance of your code. I understood that read() will keep returning 0 till client process opens it for writing ,Hence we are forced to use open() again . and to avoid that , we use the trick you have mentioned – dhruv gupta Jun 20 '18 at 6:14
  • Indeed. – JdeBP Jun 20 '18 at 7:25
  • @dhruvgupta I have edited to comment the first example a little better. Personally, I find it quite useful when I can test ideas with a few shell commands. But it does require you to know the necessary types of shell syntax :). If you want to understand the first example, I think you need "job control" (&), "input/output redirection" (<, >), and "subshell" (parentheses, ( )). ; is just a way to separate multiple commands without a new line. – sourcejedi Jun 20 '18 at 8:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.