On this page

Pipes and socketpair

Coverage: pipe → socketpair (AF_UNIX) → splice → pipe capacity → AF_UNIX vs TCP → Performance considerations Applicable to: Linux userspace IPC

Overview

Pipes and Unix domain sockets are the most commonly used local IPC mechanisms on Linux. Pipes are suitable for unidirectional data streams (parent-child processes), while socketpairs are suitable for bidirectional communication. Understanding the differences in their kernel implementations (pipes are based on pipefs, while Unix sockets are based on VFS + an independent protocol stack) is important for choosing the correct IPC mechanism.

pipe: Unidirectional Pipe

int pipefd[2];
pipe(pipefd);            // pipefd[0]=read end, pipefd[1]=write end

// Or: pipe2(pipefd, O_NONBLOCK | O_CLOEXEC);

write(pipefd[1], buf, len);  // Write
read(pipefd[0], buf, len);   // Read

// Default pipe capacity: 16 pages (64KB on 4KB pages)
// Adjustable: fcntl(fd, F_SETPIPE_SZ, size)

Inside pipe

// fs/pipe.c
// Each pipe = one pipe_inode_info:
//   Ring buffer (16 pages default)
//   Readers waiting for data → placed in wait queue → when writer arrives → wake up
//   Writers waiting for space → placed in wait queue → when reader arrives → wake up

// Pipe capacity:
cat /proc/sys/fs/pipe-max-size  # Maximum capacity (default 1MB)
fcntl(fd, F_SETPIPE_SZ, 1048576);  // Set to 1MB

splice: Zero-copy Transfer

// splice is the superpower of pipes:
//   Bypasses userspace → FD1 → pipe → FD2

// File → socket (underlying mechanism of sendfile):
splice(file_fd, &offset, pipefd[1], NULL, size, 0);  // File → pipe
splice(pipefd[0], NULL, sock_fd, NULL, size, 0);     // Pipe → socket
// → Zero-copy: Data goes directly from page cache to socket buffer

socketpair: Bidirectional Pipe

#include <sys/socket.h>
int sv[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, sv);

// sv[0] ↔ sv[1] for bidirectional communication
write(sv[0], "hello", 5);
read(sv[1], buf, 5);     // Receives "hello"

// SOCK_STREAM: Reliable, ordered, bidirectional (similar to TCP)
// SOCK_DGRAM:  Unreliable, preserves message boundaries (similar to UDP)
// SOCK_SEQPACKET: Reliable + preserves message boundaries (5.3+)

AF_UNIX Socket vs TCP

Local process-to-process communication on the same machine:
  AF_UNIX socket (Unix domain):
    Does not traverse the protocol stack → only passes sk_buff within the kernel
    Latency: ~2μs (vs TCP localhost ~10μs)
    Throughput: ~5GB/s (vs TCP localhost ~2GB/s)

  Why is AF_UNIX fast?
    1. No TCP state machine (no SYN/SYN-ACK/FIN)
    2. No checksum (not needed for local communication)
    3. No flow control/congestion control
    4. No IP fragmentation/reassembly

  AF_UNIX Credential Passing (SCM_CREDENTIALS):
    sendmsg(sock, msg, 0)  ← Passes pid/uid/gid along
    Receiver: recvmsg → SCM_CREDENTIALS → Trusts sender identity
    → DBus, systemd, polkit all rely on this

pipe vs socketpair vs eventfd

pipesocketpair (AF_UNIX)eventfd
DirectionUnidirectionalBidirectionalUnidirectional signal
DataByte streamByte stream / Message8-byte counter
Capacity64KB+Send buffer (default 200KB)1 counter
spliceSupportedNot supported (partially supported in 4.x+)Not supported
Credential PassingNoYes (SCM_CREDENTIALS)No
epollSupportedSupportedSupported

References

  • man: pipe(2), socketpair(2), splice(2), unix(7)
  • Source Code: fs/pipe.c, net/unix/af_unix.c

Keywords: pipe, socketpair, AF_UNIX, splice, SCM_CREDENTIALS, pipe capacity, eventfd