Skip to content
Home
IPC in Operating Systems: Pipes, Shared Memory, Sockets & More

IPC in Operating Systems: Pipes, Shared Memory, Sockets & More

Operating Systems Operating Systems 8 min read 1516 words Beginner ExcellentWiki Editorial Team

Interprocess Communication (IPC) is the substrate that enables independent processes to exchange data, synchronize execution, and coordinate access to shared resources. Process isolation — enforced by the memory management unit (MMU) — prevents one process from reading or corrupting another’s memory. IPC bridges this isolation with controlled communication channels. The choice of IPC mechanism directly affects application throughput, latency, and architectural complexity. This guide examines each IPC mechanism in the context of POSIX systems, with attention to performance characteristics and real-world use cases.

Pipes and FIFOs

Anonymous Pipes

The anonymous pipe is the simplest IPC mechanism: a unidirectional byte stream connecting two processes. The pipe() system call creates two file descriptors — one for the read end, one for the write end. In the common pattern, a parent process calls pipe() and then fork(). The child inherits the file descriptors. The parent closes the read end and writes to the pipe; the child closes the write end and reads from the pipe.

Pipes enforce a producer-consumer flow: a read on an empty pipe blocks until data is written; a write on a full pipe (the pipe buffer, typically 64 KB on Linux) blocks until space is available. This automatic synchronization is the pipe’s key advantage — there is no need for explicit mutexes or condition variables. The limitation is direction: a single pipe carries data one way. Bidirectional communication requires two pipes.

Named Pipes (FIFOs)

Named pipes extend the pipe abstraction to unrelated processes. A FIFO exists as a file in the filesystem, created by mkfifo() or the mkfifo command. One process opens it for writing, another opens it for reading. Named pipes persist as long as a directory entry references them. They are useful for simple client-server patterns where clients write requests to a well-known FIFO and read responses from a per-client FIFO.

The shell pipeline — ps aux | grep httpd | wc -l — is the most familiar use of anonymous pipes. Each | connects the stdout of one process to the stdin of the next through an anonymous pipe created by the shell.

Shared Memory

Shared memory is the fastest IPC mechanism because, after the initial setup, data transfer requires no kernel involvement. Multiple processes map the same physical memory pages into their virtual address space. One process writes; others see the data immediately (subject to cache coherence).

The POSIX shared memory API (shm_open, mmap) or the older System V API (shmget, shmat) creates shared memory segments. On Linux, POSIX shared memory objects appear under /dev/shm/. The typical pattern: a server process creates a shared memory object with shm_open() and ftruncate() to set the size, then maps it with mmap() and PROT_READ | PROT_WRITE and MAP_SHARED. Client processes open the same object and map it.

The critical consideration with shared memory is synchronization. Because multiple processes access the same memory concurrently, race conditions are inevitable without explicit coordination. Shared memory must be paired with a synchronization primitive — a POSIX mutex (placed in the shared memory segment itself), a semaphore, or a condition variable.

Message Queues

POSIX message queues (mq_open, mq_send, mq_receive) provide a mechanism for processes to exchange discrete, structured messages. Each message has a priority (an unsigned integer, with higher values indicating higher priority). A receiving process can request the highest-priority message, a message of a specific priority, or any message.

Unlike pipes, message queues support multiple readers and writers without the one-to-one correspondence issue. Messages persist in the queue until they are explicitly received — a process can send a message and exit, and another process can receive it later. The queue size (maximum number of messages and maximum message size) is configured at creation time through the mq_attr structure.

System V message queues (msgget, msgsnd, msgrcv) provide similar functionality with a different API and are available on a wider range of Unix systems. POSIX message queues have simpler semantics and are preferred on Linux.

Unix Domain Sockets

Unix domain sockets provide bidirectional communication between processes on the same machine. They support both stream-oriented (SOCK_STREAM, analogous to TCP) and datagram-oriented (SOCK_DGRAM, analogous to UDP) semantics. Stream sockets provide reliable, ordered data delivery; datagram sockets preserve message boundaries.

Unix domain sockets are significantly faster than TCP loopback (127.0.0.1) for local communication. TCP incurs protocol overhead (headers, checksums, congestion control) that is unnecessary when both endpoints are on the same kernel. Unix sockets bypass the network stack entirely, transferring data directly through the kernel’s socket buffer infrastructure. The pathname used for binding (/tmp/app.sock) must be unique within the filesystem.

The sendmsg()/recvmsg() system calls with Unix domain sockets support passing ancillary data (cmsg), including file descriptors (SCM_RIGHTS) and credentials (SCM_CREDENTIALS). Passing a file descriptor allows one process to grant another access to a file, pipe, or socket that it holds — a powerful capability for privilege separation in security-sensitive applications.

Signals

Signals are asynchronous notifications sent to a process by the kernel or another process. Each signal has a default disposition (terminate, core dump, ignore, stop, continue) that can be overridden by a custom signal handler. The kill() system call sends a signal to a process or process group.

Signals convey minimal information — only the signal number. They are used for control flow (SIGTERM requests graceful termination, SIGINT interrupts the foreground process, SIGHUP signals terminal disconnect) rather than data transfer. The limited payload and the restrictions on what system calls can be safely used inside signal handlers (the async-signal-safe set) make signals unsuitable for general IPC.

Memory-Mapped Files

The mmap() system call maps a file — or a portion of a file — into the calling process’s virtual address space. When multiple processes map the same file with MAP_SHARED, changes made by one process are visible to the others. The file system serves as the backing store: data written through the mapped memory is eventually flushed to disk (configurable with msync()).

Memory-mapped files combine the persistence of file I/O with the performance of memory access. They are the mechanism the kernel uses to load executables and shared libraries. Large databases use mmap for buffer pool management. The performance advantage over read()/write() is that the kernel manages the page cache transparently — frequently accessed pages remain in physical memory without explicit read calls.

Performance Comparison

The latency hierarchy of IPC mechanisms on modern Linux (rough figures for a single-byte round trip):

MechanismLatency (approx)Kernel involvement
Shared memory50–100 nsSetup only
mmap (same page)100–200 nsSetup only
Unix stream socket1–5 μsPer operation
Pipe2–6 μsPer operation
POSIX message queue3–8 μsPer operation
TCP loopback6–15 μsFull stack
Signal10–20 μsPer delivery

Shared memory is 100–300× faster than pipes or sockets for data transfer. The trade-off is the complexity of manual synchronization and the risk of programming errors that produce data races or deadlocks.

FAQ

What is the fastest IPC mechanism, and when should I use it?

Shared memory is the fastest IPC mechanism because, after initial setup, data moves directly between process address spaces without kernel mediation. Use shared memory when the primary requirement is throughput — streaming large data volumes (video frames, sensor data, database buffer pools) between processes on the same machine. Use sockets or pipes when simplicity and portability matter more than peak throughput.

How do Unix domain sockets compare to TCP sockets for local IPC?

Unix domain sockets are 50–80% faster than TCP loopback for local communication because they bypass the network stack (no protocol headers, no checksums, no congestion control). They support ancillary data (file descriptor passing, credentials). The limitation is that they only work on the same machine, while TCP works across networks.

Can shared memory be used between processes running as different users?

Yes, if the shared memory object’s permissions (specified in the mode argument to shm_open() or shmget()) allow access. POSIX shared memory objects in /dev/shm have standard Unix permission bits. For security-sensitive applications, consider using Unix domain sockets with credential checking instead.

What causes a broken pipe signal (SIGPIPE)?

A process receives SIGPIPE when it writes to a pipe, socket, or FIFO whose read end has been closed. The default disposition of SIGPIPE is to terminate the process. Pipe-using programs either handle SIGPIPE (ignore it) or check the return value of write() — EPIPE indicates the read end is closed.

How do I choose between message queues and sockets?

Message queues are simpler for multi-producer/multi-consumer patterns where each message is independent and priority ordering is useful. Sockets are more flexible — they support bidirectional streams, out-of-band data, ancillary data, and connection-oriented flow control. For new development, Unix domain sockets are generally preferred over POSIX message queues because they compose better with poll/select/epoll and are available on all POSIX systems.


The Process Scheduling Guide explains how the scheduler interacts with blocked IPC operations (processes waiting on a pipe read are in the waiting state). The Synchronization and Concurrency Guide covers the mutexes, semaphores, and condition variables that must accompany shared memory IPC. Stevens and Rago’s Advanced Programming in the UNIX Environment, 3rd Edition, devotes Chapters 15–17 to complete coverage of IPC with validated source code examples.

Section: Operating Systems 1516 words 8 min read Beginner 756 articles in section Report inaccuracy Back to top