pipe(2)

๐Ÿ’– pipe โ€” create pipe ๐Ÿ’–

okay bestie, come sit with me because we're about to talk about pipe(2), which lives in section 2 of the manual (System Calls Manual, if you're fancy). two functions, one iconic duo: pipe and pipe2. they make pipes. that's the whole vibe. ๐Ÿฆ‹


๐Ÿ“š LIBRARY

Standard C library (libc, -lc). yeah she's built in. no extra credit needed. ๐Ÿ’…


โœจ SYNOPSIS

#include <unistd.h>

int pipe(int pipefd[2]);

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <fcntl.h>              /* Definition of O_* constants */
#include <unistd.h>

int pipe2(int pipefd[2], int flags);

/* On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64, pipe() has the
   following prototype; see VERSIONS */

#include <unistd.h>

struct fd_pair {
    long fd[2];
};
struct fd_pair pipe(void);

okay so two of these are normal, and then there's the chaos one at the bottom for the special architectures. stick around for VERSIONS, it's a whole saga. ๐Ÿ˜ญ


๐ŸŒธ DESCRIPTION

so pipe() creates a pipe โ€” a unidirectional data channel you can use for interprocess communication. one-way street, no notes. ๐Ÿซถ

the array pipefd is where she drops off two file descriptors pointing at the two ends of the pipe:

  • pipefd[0] โ†’ the read end of the pipe.
  • pipefd[1] โ†’ the write end of the pipe.

anything you write to the write end gets buffered by the kernel until somebody reads it from the read end. no cap. for the deep lore, go read pipe(7).

if flags is 0, then pipe2() is literally just pipe(). same girl, different name. otherwise you can bitwise OR the following values together in flags to get different behavior:

๐ŸŽ€ the flags (yes this counts as OPTIONS)

  • O_CLOEXEC โ€” sets the close-on-exec (FD_CLOEXEC) flag on both new file descriptors. if you wanna know why that's useful, go read the description of the same flag in open(2), she explains it better.
  • O_DIRECT (since Linux 3.4) โ€” makes a pipe that does I/O in "packet" mode. every write(2) to the pipe becomes its own separate packet, and read(2)s pull one packet at a time. a few things to note, bestie, don't skip these:
    • Writes bigger than PIPE_BUF bytes (see pipe(7)) get split into multiple packets. PIPE_BUF is defined in <limits.h>.
    • If a read(2) gives a buffer size smaller than the next packet, it reads the number of bytes you asked for and the excess bytes in the packet are discarded (gone, deleted, not coming back). giving a buffer size of PIPE_BUF is enough to read the biggest possible packets โ€” see the previous point.
    • Zero-length packets are not supported. (a read(2) with a buffer size of zero is a no-op and returns 0.)

older kernels that don't support this flag will tell you by returning an EINVAL error.

since Linux 4.5, you can change the O_DIRECT setting of a pipe file descriptor with fcntl(2).

  • O_NONBLOCK โ€” sets the O_NONBLOCK file status flag on the open file descriptions the new file descriptors refer to. using this saves you extra calls to fcntl(2) to get the same result. convenience girlie. ๐Ÿ’…
  • O_NOTIFICATION_PIPE โ€” since Linux 5.8, the general notification mechanism is built on top of the pipe, where the kernel splices notification messages into pipes opened by user space. the owner of the pipe has to tell the kernel which sources of events to watch, and filters can also be applied to pick which subevents get placed into the pipe. very main character of the kernel honestly.

๐Ÿซถ RETURN VALUE

  • on success: 0 is returned. ate. โœจ
  • on error: -1 is returned, errno gets set to say what went wrong, and pipefd is left unchanged.

on Linux (and other systems), pipe() does not modify pipefd on failure. a requirement standardizing this behavior was added in POSIX.1-2008 TC2. and the Linux-specific pipe2() system call also doesn't modify pipefd on failure. consistent queens. ๐Ÿ‘‘


๐Ÿ’€ ERRORS

  • EFAULT โ€” pipefd is not valid.
  • EINVAL โ€” (pipe2()) invalid value in flags.
  • EMFILE โ€” the per-process limit on the number of open file descriptors has been reached.
  • ENFILE โ€” the system-wide limit on the total number of open files has been reached.
  • ENFILE โ€” the user hard limit on memory that can be allocated for pipes has been reached and the caller is not privileged; see pipe(7).
  • ENOPKG โ€” (pipe2()) O_NOTIFICATION_PIPE was passed in flags and support for notifications (CONFIG_WATCH_QUEUE) is not compiled into the kernel.
yes, ENFILE shows up twice on purpose. that's how the source has it, and we respect the source. ๐Ÿ˜Œ

๐ŸŒˆ VERSIONS

okay so here's the lore drop. the System V ABI on some architectures lets you use more than one register for returning multiple values, and several architectures โ€” namely Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64 โ€” (ab)use this feature to implement the pipe() system call in a functional manner: the call takes no arguments and returns a pair of file descriptors as its return value on success. yes it's weird. no we're not judging (we are a little). ๐Ÿ™ˆ

the glibc pipe() wrapper function transparently deals with this for you, so you don't have to think about it. if you want to know about the registers used for storing the second file descriptor, see syscall(2).


โญ STANDARDS

POSIX.1-2024. periodt.


๐ŸŽ€ HISTORY

  • pipe() โ€” POSIX.1-2001.
  • pipe2() โ€” POSIX.1-2024. Linux 2.6.27, glibc 2.9.

๐Ÿงš EXAMPLES

so this program creates a pipe, then fork(2)s to make a child process. the child inherits a duplicate set of file descriptors referring to the same pipe. after the fork(2), each process closes the file descriptors it doesn't need for the pipe (see pipe(7)). then the parent writes the string from the program's command-line argument to the pipe, and the child reads that string one byte at a time and echoes it on standard output. it's giving "hello from the other side" but make it file descriptors. ๐Ÿฆ‹

๐Ÿ’œ Program source

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
    int    pipefd[2];
    char   buf;
    pid_t  cpid;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <string>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (pipe(pipefd) == -1)
        err(EXIT_FAILURE, "pipe");

    cpid = fork();
    if (cpid == -1)
        err(EXIT_FAILURE, "fork");

    if (cpid == 0) {    /* Child reads from pipe */
        if (close(pipefd[1]) == -1)  /* Close unused write end */
            err(EXIT_FAILURE, "close");

        while (read(pipefd[0], &buf, 1) > 0) {
            if (write(STDOUT_FILENO, &buf, 1) != 1)
                err(EXIT_FAILURE, "write");
        }

        if (write(STDOUT_FILENO, "\n", 1) != 1)
            err(EXIT_FAILURE, "write");
        if (close(pipefd[0]) == -1)
            err(EXIT_FAILURE, "close");
        _exit(EXIT_SUCCESS);

    } else {            /* Parent writes argv[1] to pipe */
        if (close(pipefd[0]) == -1)  /* Close unused read end */
            err(EXIT_FAILURE, "close");
        if (write(pipefd[1], argv[1], strlen(argv[1])) != strlen(argv[1]))
            err(EXIT_FAILURE, "write");
        if (close(pipefd[1]) == -1)  /* Reader will see EOF */
            err(EXIT_FAILURE, "close");
        if (wait(NULL) == -1)        /* Wait for child */
            err(EXIT_FAILURE, "wait");
        exit(EXIT_SUCCESS);
    }
}

๐Ÿ‘‘ SEE ALSO

fork(2), read(2), socketpair(2), splice(2), tee(2), vmsplice(2), write(2), popen(3), pipe(7)


and that's the whole page bestie ๐Ÿ’– this was Linux man-pages 6.20, dated 2026-02-08, and it's from pipe(2) โ€” go make some pipes, you've got this. mwah. ๐ŸŽ€๐Ÿฆ‹๐Ÿ’…

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: Linux-man-pages-copyleft
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms