~/learn

Pipes and FIFOs

Unix pipes as unidirectional byte streams — anonymous pipes for related processes and named pipes (FIFOs) for unrelated ones.

A pipe is a unidirectional byte stream: one process writes into it, another reads out. It is the oldest form of Unix IPC and the mechanism behind the shell's | operator. This week covers both anonymous pipes (between related processes) and named pipes / FIFOs (between any processes).

What is a pipe?

Pipes embody the Unix philosophy — small programs composed through text streams. Key properties:

  • Unidirectional — data flows one way; for two-way communication you use two pipes.
  • Byte stream — no message boundaries, just a continuous stream of bytes.
  • Buffered — the kernel holds the data (typically 64 KiB on Linux).
  • Self-synchronizingread blocks when the pipe is empty; write blocks when the buffer is full.
  • EOFread returns 0 once all write ends are closed.

The shell wires pipes for you: in ls | grep txt | wc -l, each | connects one command's stdout to the next command's stdin.

Anonymous pipes

Created with pipe(fd), which fills a two-element array: fd[0] is the read end and fd[1] is the write end. Because file descriptors are inherited across fork, this connects a parent and child:

int fd[2];
pipe(fd);
if (fork() == 0) {          // child: reads
    close(fd[1]);           // close unused write end
    char buf[64];
    ssize_t n = read(fd[0], buf, sizeof buf);
    write(STDOUT_FILENO, buf, n);
    close(fd[0]);
} else {                    // parent: writes
    close(fd[0]);           // close unused read end
    write(fd[1], "hello", 5);
    close(fd[1]);           // reader now sees EOF
}

Always close unused ends

Each process must close the pipe end it doesn't use. If a write end stays open somewhere, the reader never sees EOF and blocks forever; leaked descriptors also waste resources.

Combining pipe + fork + dup2 + exec is exactly how a shell implements command1 | command2 — redirect command1's stdout into the pipe and command2's stdin out of it.

Named pipes (FIFOs)

A FIFO is a pipe with a name in the filesystem, created by mkfifo(path, mode) (or the mkfifo shell command). It shows up in ls -l with type p, and it lets unrelated processes talk — any process that can open the path can use it:

mkfifo /tmp/chan
cat < /tmp/chan          # reader (blocks until a writer opens it)
echo "hi" > /tmp/chan    # writer, from another terminal

Opening a FIFO blocks until the other end is also opened, unless you pass O_NONBLOCK. The data still lives in kernel memory; only the name persists on disk.

Practice / lab

  • Send a message parent → child through an anonymous pipe, closing unused ends on both sides.
  • Use two pipes for bidirectional communication: parent sends a number, child squares it and sends it back.
  • Reimplement command1 | command2 with pipe, fork, dup2, and exec.
  • Create a FIFO with mkfifo and exchange messages between two separate programs.

Homework

References & resources

Required

Optional / enrichment

Self-check

  1. What is the difference between an anonymous pipe and a FIFO?
  2. Why must both processes close the pipe ends they don't use?
  3. What happens when you write to a pipe with no readers?
  4. What does read return once all write ends are closed?
  5. How do you achieve bidirectional communication with pipes?

On this page