pipe(2)

System call

Creates a one-way channel in the kernel and returns two descriptors, one for reading and one for writing. Combined with fork, it connects two processes.

Synopsis
#include <unistd.h>
int pipe(int pipefd[2]);

Description

pipe creates a one-way communication channel and fills the array you pass it with two file descriptors: pipefd[0] is the read end, pipefd[1] the write end. What you write into pipefd[1] with write comes out of pipefd[0] with read, in the same order. In between, the data waits in a kernel buffer (16 pages on Linux, so 65,536 bytes with 4 KiB pages).

On its own, a pipe is not much use. Created before a fork, it is inherited by the child, and both processes then hold both ends of the same pipe. One writes, the other reads, and each one closes the end it does not use. That is the mechanism behind the shell's |, with dup2 to plug the pipe into standard input or standard output.

Return value

0 on success. -1 on error, with errno (EMFILE when the process has reached its limit of open descriptors, ENFILE for the system-wide limit); the array is then left unchanged.

Pitfalls

  • read returns 0, the end of the stream, only once every write end is closed, in every process. A pipefd[1] left open by mistake in the parent is enough to block the reader forever.

  • Writing to a pipe whose read end nobody holds anymore sends the SIGPIPE signal, which terminates the process by default.

  • A full pipe blocks write until the reader consumes some data. Two processes that write to each other without reading can block each other.

  • Both ends are ordinary descriptors: remember to close them like any file.

Example

pipe.c
#include <stdio.h>#include <sys/wait.h>#include <unistd.h>
int main(void){    int fd[2];    char buf[64];    ssize_t n;
    if (pipe(fd) == -1) {        perror("pipe");        return 1;    }    if (fork() == 0) {        close(fd[1]);        while ((n = read(fd[0], buf, sizeof buf)) > 0)            write(STDOUT_FILENO, buf, n);        close(fd[0]);        return 0;    }    close(fd[0]);    write(fd[1], "hello from parent\n", 18);    close(fd[1]);    wait(NULL);    return 0;}
Terminal
$ gcc -Wall -Wextra pipe.c -o pipe$ ./pipehello from parent

The child closes the write end and reads until the end of the stream; the parent closes the read end, writes, then closes the write end. That last close is what lets the child's loop finish.

C

pipe_chain

Write pipe_chain, which puts a pipe between each pair of commands in a list and closes every end a process no longer needs.

Open the exercise

Now write it yourself.