I think you're getting mixed up between shell syntax for pipelines vs. the underlying Unix systems programming. A pipe / FIFO is a type of file that isn't stored on disk, but instead passed data from a writer to a reader through a buffer in the kernel.
A pipe/FIFO works the same whether the writer and reader got connected by making system calls like open("/path/to/named_pipe", O_WRONLY);
, or with a pipe(2)
to create a new anonymous pipe and return open file descriptors to both the read and write sides.
fstat(2)
on the pipe file descriptor will give you sb.st_mode & S_IFMT == S_IFIFO
either way.
When you run foo | bar
:
- The shell forks like normal for any non-builtin command
- Then makes a
pipe(2)
system call to get two file descriptors: the input and output of an anonymous pipe.
- Then it forks again.
- The child (where
fork()
returned 0)
- closes the read side of the pipe (leaving the write fd open)
- and redirects
stdout
to the write fd with dup2(pipefd[1], 1)
- then does
execve("/usr/bin/foo", ...)
- The parent (where
fork()
returned the non-0 child PID)
- closes the write side of the pipe (leaving the read fd open)
- and redirects
stdin
from the read fd with dup2(pipefd[0], 0)
- then does
execve("/usr/bin/bar", ...)
You get into a very similar situation if you run foo > named_pipe & bar < named_pipe
.
A named pipe is a rendezvous for processes to establish pipes between each other.
The situation is similar to anonymous tmp files vs. files with names. You can open("/path/to/dir", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
to create a temporary file with no name (O_TMPFILE
), as if you'd opened "/path/to/dir/tmpfile"
with O_CREAT
and then unlinked it, leaving you with a file descriptor to a deleted file.
Using linkat
, you can even link that anonymous file into the filesystem, giving it a name, if it was created with O_TMPFILE
. (You can't do this on Linux for files that you created with a name then deleted, though.)