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 inopen(2), she explains it better.O_DIRECT(since Linux 3.4) โ makes a pipe that does I/O in "packet" mode. everywrite(2)to the pipe becomes its own separate packet, andread(2)s pull one packet at a time. a few things to note, bestie, don't skip these:- Writes bigger than
PIPE_BUFbytes (seepipe(7)) get split into multiple packets.PIPE_BUFis 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 ofPIPE_BUFis 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 returns0.)
- Writes bigger than
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 theO_NONBLOCKfile status flag on the open file descriptions the new file descriptors refer to. using this saves you extra calls tofcntl(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:
0is returned. ate. โจ - on error:
-1is returned,errnogets set to say what went wrong, andpipefdis 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โpipefdis not valid.EINVALโ (pipe2()) invalid value inflags.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; seepipe(7).ENOPKGโ (pipe2())O_NOTIFICATION_PIPEwas passed inflagsand 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
- Copyright 2005-2008, Michael Kerrisk <mtk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms