Named pipes, file descriptors and EOF

Two windows, same user, with bash prompts. In window-1 type:

$ mkfifo f; exec <f

So bash is now attempting to read from file descriptor 0, which is mapped to named pipe f. In window-2 type:

$ echo ls > f

Now window-1 prints an ls and then the shell dies. Why?

Next experiment: open window-1 again with exec <f. In window-2 type:

$ exec 3>f
$ echo ls >&3

After the first line above, window-1 wakes up and prints a prompt. Why? After the second line above, window-1 prints the ls output and the shell stays alive. Why? In fact, now in window-2, echo ls > f does not close the window-1 shell.

The answer must have to do with the existence of the file descriptor 3 from window-2 referencing the named pipe?!

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

It has to do with the closing of the file descriptor.

In your first example, echo writes to its standard output stream which the shell opens to connect it with f, and when it terminates, its descriptor is closed (by the shell). On the receiving end, the shell, which reads input from its standard input stream (connected to f) reads ls, runs ls and then terminates due to the end-of-file condition on its standard input.

The end-of-file condition occurs because all writers to the named pipe (only one in this example) have closed their end of the pipe.

In your second example, exec 3>f opens file descriptor 3 for writing to f, then echo writes ls to it. It’s the shell that now has the file descriptor opened, not the echo command. The descriptor remains open until you do exec 3>&-. On the receiving end, the shell, which reads input from its standard input stream (connected to f) reads ls, runs ls and then waits for more input (since the stream is still open).

The stream remains open because all writers to it (the shell, via exec 3>f, and echo) have not closed their end of the pipe (exec 3>f is still in effect).


I have written about echo above as if it was an external command. It’s most likely is built into the shell. The effect is the same nonetheless.

Method 2

There’s not much to it: when there are no writers to the pipe, it looks closed to readers, i.e. returns EOF when read and blocks when opened.

From the Linux man page (pipe(7), but see also fifo(7)):

If all file descriptors referring to the write end of a pipe have
been closed, then an attempt to read(2) from the pipe will see end-
of-file (read(2) will return 0).

Closing the write end is what implicitly happens at the end of the the echo ls >f, and as you say, in the other case, the file descriptor is kept open.

Method 3

After reading the two answers from @Kusalananda and @ikkachu, I think I understand. In window-1, the shell is waiting for something to both open the write end of the pipe and then close it. Once the write end is opened, the shell in window-1 prints a prompt. Once the write end is closed, the shell gets EOF and dies.

On the window-2 side we have the two situations described in my question: in the first situation with echo ls > f, there is no file descriptor 3, so we have echo spawning, and its stdin and stdout look like this:

0 --> tty
1 --> f

Then echo terminates and the shell closes both descriptors. Since file descriptor 1 is closed and references f, the write end of f is closed and that causes an EOF to window-1.

In the second situation, we run exec 3>f in our shell, causing the shell to take this environment:

bash:
0 --> tty
1 --> tty
2 --> tty
3 --> f

Now we run echo ls >& 3 and the shell allocates file descriptors for echo as follows:

echo:
0 --> tty
1 --> f     # because 3 points to f
2 --> tty

Then the shell closes the three descriptors above, including f, but f still has a reference to it from the shell itself. This is the important difference. Closing descriptor 3 with exec 3>&- would close the last open reference and cause an EOF to window-1, as @Kusalananda noted.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x