pipe2()
2.6.27+
Intermediate
pipe2() โ Linux-Specific Enhancement
Linux kernel 2.6.27 introduced pipe2(), a non-standard extension to pipe(). It does everything pipe() does, plus supports an optional flags argument that can atomically set important properties on the file descriptors.
Two flags are supported: O_CLOEXEC (close on exec) and O_NONBLOCK (nonblocking I/O). Both solve race conditions that would otherwise require separate syscalls after pipe().
Key Concepts
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
int pipe2(int filedes[2], int flags);
/* Returns 0 on success, -1 on error */
/* flags can be:
* 0 โ same as pipe()
* O_CLOEXEC โ set FD_CLOEXEC on both fds
* O_NONBLOCK โ set O_NONBLOCK on both fds
* O_CLOEXEC | O_NONBLOCK โ both at once
*/
If flags = 0, pipe2() behaves exactly like pipe(). The benefit is that flags are set atomically at creation time โ no race window between pipe() and a subsequent fcntl() call.
By default, file descriptors survive across exec() calls. This means if a process creates a pipe and then calls exec() to replace itself with another program, the new program inherits the pipe descriptors โ which is almost never what you want.
FD_CLOEXEC (the close-on-exec flag) tells the kernel to automatically close a file descriptor when exec() is called.
2. fork() + exec() โ new program runs
3. fd[3] and fd[4] still open in new program
4. Leaked FDs โ pipe never closes โ bugs
2. fork() + exec() โ new program runs
3. fd[3] and fd[4] automatically closed
4. No leaks, correct behavior
Example 1: pipe2() with O_CLOEXEC
/* cloexec_demo.c */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(void)
{
int pfd[2];
/* Create pipe with close-on-exec flag */
if (pipe2(pfd, O_CLOEXEC) == -1) {
perror("pipe2");
return 1;
}
printf("Created pipe: fd[0]=%d, fd[1]=%d\n", pfd[0], pfd[1]);
/* Verify FD_CLOEXEC is set */
int flags0 = fcntl(pfd[0], F_GETFD);
int flags1 = fcntl(pfd[1], F_GETFD);
printf("fd[0] FD_CLOEXEC set: %s\n", (flags0 & FD_CLOEXEC) ? "YES" : "NO");
printf("fd[1] FD_CLOEXEC set: %s\n", (flags1 & FD_CLOEXEC) ? "YES" : "NO");
/* If we exec() here, pfd[0] and pfd[1] would be auto-closed */
/* Compare: old way needed two extra fcntl() calls */
int pfd2[2];
pipe(pfd2);
/* Old way to set close-on-exec: */
fcntl(pfd2[0], F_SETFD, FD_CLOEXEC); /* extra call 1 */
fcntl(pfd2[1], F_SETFD, FD_CLOEXEC); /* extra call 2 */
/* pipe2(O_CLOEXEC) does this atomically! */
close(pfd[0]); close(pfd[1]);
close(pfd2[0]); close(pfd2[1]);
return 0;
}
/* Output:
Created pipe: fd[0]=3, fd[1]=4
fd[0] FD_CLOEXEC set: YES
fd[1] FD_CLOEXEC set: YES
*/
By default, pipes block: read() on an empty pipe waits, write() on a full pipe waits. With O_NONBLOCK, these operations fail immediately with errno = EAGAIN instead of blocking.
| Operation | Blocking (default) | Nonblocking (O_NONBLOCK) |
|---|---|---|
| read() on empty pipe | Blocks until data available | Returns -1, errno = EAGAIN |
| write() on full pipe | Blocks until space available | Returns -1, errno = EAGAIN |
| read() on closed write end | Returns 0 (EOF) | Returns 0 (EOF) โ same |
Example 2: Nonblocking pipe with pipe2()
/* nonblocking_pipe.c */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main(void)
{
int pfd[2];
/* Create nonblocking pipe */
if (pipe2(pfd, O_NONBLOCK) == -1) {
perror("pipe2");
return 1;
}
printf("=== Test 1: Nonblocking read on empty pipe ===\n");
char buf[64];
ssize_t n = read(pfd[0], buf, sizeof(buf));
if (n == -1) {
if (errno == EAGAIN)
printf("read() returned EAGAIN (no data yet) โ nonblocking works!\n");
else
perror("read");
}
printf("\n=== Test 2: Write data, then read ===\n");
const char *msg = "Nonblocking pipe test";
write(pfd[1], msg, strlen(msg));
n = read(pfd[0], buf, sizeof(buf) - 1);
buf[n] = '\0';
printf("Read after write: \"%s\"\n", buf);
printf("\n=== Test 3: Fill the pipe to test write blocking ===\n");
/* Fill up the pipe buffer to test O_NONBLOCK on write */
int total = 0;
char fill[4096];
memset(fill, 'X', sizeof(fill));
while (1) {
n = write(pfd[1], fill, sizeof(fill));
if (n == -1) {
if (errno == EAGAIN) {
printf("write() returned EAGAIN โ pipe is full (%d bytes written)\n",
total);
break;
}
perror("write");
break;
}
total += n;
}
close(pfd[0]);
close(pfd[1]);
return 0;
}
/* Output:
=== Test 1: Nonblocking read on empty pipe ===
read() returned EAGAIN (no data yet) โ nonblocking works!
=== Test 2: Write data, then read ===
Read after write: "Nonblocking pipe test"
=== Test 3: Fill the pipe to test write blocking ===
write() returned EAGAIN โ pipe is full (65536 bytes written)
*/
Example 3: Set O_NONBLOCK on existing pipe using fcntl()
/* set_nonblock_fcntl.c โ when you can't use pipe2() */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/* Helper: make an fd nonblocking */
int set_nonblocking(int fd)
{
int flags = fcntl(fd, F_GETFL);
if (flags == -1) return -1;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int main(void)
{
int pfd[2];
pipe(pfd); /* regular pipe */
/* Make both ends nonblocking */
set_nonblocking(pfd[0]);
set_nonblocking(pfd[1]);
/* Test: read from empty nonblocking pipe */
char buf[64];
ssize_t n = read(pfd[0], buf, sizeof(buf));
if (n == -1 && errno == EAGAIN)
printf("EAGAIN โ empty pipe, nonblocking set via fcntl\n");
close(pfd[0]);
close(pfd[1]);
return 0;
}
| Feature | pipe() | pipe2(flags) |
|---|---|---|
| POSIX standard | โ Yes | โ Linux-specific |
| Available since | Always | Kernel 2.6.27 |
| O_CLOEXEC support | Needs extra fcntl() | โ Atomic |
| O_NONBLOCK support | Needs extra fcntl() | โ Atomic |
| Race-condition safe | โ (window between pipe+fcntl) | โ Flags set atomically |
| Header required | <unistd.h> | <unistd.h> + <fcntl.h> + #define _GNU_SOURCE |
A: pipe2() is a Linux-specific (kernel 2.6.27+) variant of pipe() that accepts a flags argument. It can atomically set O_CLOEXEC (close-on-exec) and/or O_NONBLOCK on the created file descriptors. This avoids the race condition present between pipe() and a subsequent fcntl() call.
A: O_CLOEXEC sets the FD_CLOEXEC flag on a file descriptor. This causes the fd to be automatically closed when the process calls exec(). It prevents fd leaks into child programs that shouldn’t have access to the pipe.
A: EAGAIN (or EWOULDBLOCK) is the errno set when a nonblocking I/O operation cannot complete immediately. A nonblocking read() returns EAGAIN when the pipe is empty; a nonblocking write() returns EAGAIN when the pipe is full.
A: Use
fcntl(fd, F_GETFL) to read current flags, then fcntl(fd, F_SETFL, flags | O_NONBLOCK) to add O_NONBLOCK. This must be done for each end separately.A: In a multithreaded program, between the pipe() call and the subsequent fcntl(F_SETFD, FD_CLOEXEC) call, another thread could call fork() + exec(). The new program would inherit the unprotected fd. pipe2(O_CLOEXEC) sets the flag atomically inside the kernel, eliminating this window.
