4

I've been reading about unnamed pipes and as I understood them they're implemented as a buffer in memory. When creating the pipe I need to pass an array of size two and it returns two pointers (file descriptors) to the buffer. The index 0 is used for reading from the pipe and index 1 for writing to it.

My question is, if the buffer is just one and both indexes point to the same memory location and two processes can't read and write at the same time, then why do I need two file descriptors? I hope my question makes sense.

  • 2
    I'm not sure if I understand your question, but wouldn't it be because you should be connecting one to stdin and one to stdout? i.e., the process you're writing should read off one and write out to the other, so why make the programmer handle that scaffolding manually. Or in other words: the nature of a pipe is having an input and an output. – Mel Boyce Jun 19 '13 at 12:05
5

As stated by @MelBoyce in his comment, this is because of the conceptual nature of a pipe. It has a input and an output, and you read the bytes from the output in the exact same order they were written into the input. Pipes are not common files or pointers, you're not suppose to read and write anywhere in it. You're forced to read the first bytes that entered the pipe and that never be read yet.

Pipes may be implemented as a buffer in memory, but the implementation could vary if in the future, if another more efficient way to do it is invented for example. However, the conceptual nature of the pipe won't change. You'll still use the same read(1) and write(1) system calls and they still behave the same. The file descriptors you get when you call pipe(1) are used to force you to use the pipe correctly (and additionally provide some access control). That way, future modifications in the implementation won't break your code.

  • Furthermore, it doesn't make sense to focus on the pipe from the perspective of a single process. A pipe is an interprocess communication mechanism; it only makes sense to use when the reading process requires information from the writing process. – tripleee Jun 19 '13 at 15:30
2

An important feature would be missing if pipe returned a single file descriptor to be used for both reading and writing: there would be no way to signal EOF.

With pipes as they are, the read fd sees EOF when the last copy of the write fd is closed. With hypothetical single-fd pipes, you'd need an extra syscall, like shutdown for sockets, but applied to a pipe. (Remember that pipes are a older than sockets, and shutdown didn't exist before sockets.)

And who would be responsible for calling this pipe_shutdown() syscall? Suppose you do this:

grep foo /etc/passwd | head

If there's only a single fd, which grep and head have both inherited, then somehow when grep exits, head needs to receive an EOF. But the fd held by the head process is writable, so the kernel doesn't know for sure that nothing more will be written to the pipe. You may be thinking: process exit should automatically send EOF on all pipe fds held by the process. But that'll break a lot of stuff, like

( echo FOO ; grep foo /etc/passwd ) | more

echo writes to the pipe and exits, and we don't want an EOF to happen yet. grep is still going to write to the pipe. And that problem isn't limited to the "obscure" case of parenthesized subshells. It can happen any time a shell script is used on the left side of a pipe.

If you'll think it over, the pipe EOF mechanism really requires a distinction between fds that can write to the pipe and fds that can only read it. And that's not dependent on how many processes are involved.

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.