PIPE(2)

πŸ’– PIPE(2) β€” the manual page, but make it cute πŸ’–

okay bestie, sit down, we're learning about pipe and pipe2 from the System Calls Manual. this is the page for creating a descriptor pair for interprocess communication, and honestly? it's giving plumbing but make it glamorous. 🫢


✨ NAME ✨

pipe, pipe2 β€” create descriptor pair for interprocess communication


πŸ“š LIBRARY πŸ“š

Standard C Library (libc, -lc)


πŸŽ€ SYNOPSIS πŸŽ€

#include <unistd.h>

int
pipe(int fildes[2]);

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

🌸 DESCRIPTION 🌸

so. the pipe() function creates a pipe, which is literally an object that lets data flow bidirectionally, and it allocates a pair of file descriptors. two fds, one dream. ✨

the pipe2() system call is the main character version that lets you control the attributes of those file descriptors through the flags argument. you build the values for flags by doing a bitwise-inclusive OR of flags from this list, which lives in <fcntl.h>:

  • O_CLOEXEC β€” sets the close-on-exec flag for the new file descriptors. so they get yeeted on exec. πŸ’…
  • O_NONBLOCK β€” sets the non-blocking flag for the ends of the pipe.

if the flags argument is 0, the behavior is identical to a call to pipe(). no cap, same thing.

by convention (and this matters, bestie), the first descriptor is normally used as the read end of the pipe, and the second is normally the write end. so data written to fildes[1] appears on β€” meaning it can be read from β€” fildes[0]. the way that works out is that the output of one program can get sent to another program: the source's standard output gets set up as the write end of the pipe, and the sink's standard input gets set up as the read end of the pipe. iconic behavior tbh. the pipe itself persists until all its associated descriptors are closed. πŸ¦‹

now here's the sad part 😭 β€” a pipe that has had an end closed is considered widowed. writing on such a pipe causes the writing process to receive a SIGPIPE signal. widowing a pipe is the only way to deliver end-of-file to a reader: after the reader consumes any buffered data, reading a widowed pipe returns a zero count.

🚨 heads up! the bidirectional nature of this implementation of pipes is not portable to older systems, so it's recommended that you use the convention for using the endpoints in the traditional manner when you're using a pipe in one direction. stay safe out there. πŸ™ˆ

🧚 IMPLEMENTATION NOTES 🧚

the pipe() function calls the pipe2() system call. as a result, system call traces β€” like the ones captured by dtrace(1) or ktrace(1) β€” will show calls to pipe2(). so if you're reading a trace and you're like "bestie I called pipe()??" … yeah, it's pipe2() under the hood. iykyk. πŸ’€


⭐ RETURN VALUES ⭐

the pipe() function returns the value 0 if successful; otherwise the value -1 is returned and the global variable errno is set to indicate the error. simple as that. πŸ’œ


πŸ’€ ERRORS πŸ’€

the pipe() and pipe2() system calls will fail if:

  • [EFAULT] β€” the fildes argument points to an invalid memory location. 🚩
  • [EMFILE] β€” too many descriptors are active.
  • [ENFILE] β€” the system file table is full. 😡
  • [ENOMEM] β€” not enough kernel memory to establish a pipe.

the pipe2() system call will also fail if:

  • [EINVAL] β€” the flags argument is invalid.

πŸ”₯ SEE ALSO πŸ”₯

sh(1), fork(2), read(2), socketpair(2), write(2)


πŸ‘‘ HISTORY πŸ‘‘

  • the pipe() function appeared in Version 3 AT&T UNIX.
  • bidirectional pipes were first used on AT&T System V Release 4 UNIX.
  • the pipe2() function appeared in FreeBSD 10.0.
  • the pipe() function became a wrapper around pipe2() in FreeBSD 11.0.

and that's the whole page, bestie!! you ate that. πŸ“ this little gem comes to you from Debian, dated December 1, 2017, and it lives in section PIPE(2) of the manual. go forth and pipe responsibly πŸ’–πŸŒˆβœ¨

πŸ“¦ where this page comes from