Progress pill
User level

Inter-process communication options

System Programming Fundamentals

Inter-process communication options

  • Pipes
  • Named pipes (FIFO)
  • Signals
  • Sockets
  • Message queues
  • Shared memory and semaphores
Most Unix systems end up running lots of small processes that cooperate. Sometimes that cooperation is explicit, like a shell running one program and feeding its output into another. Sometimes it is hidden, like a desktop application talking to a background service, or a web server handing work to worker processes. This is what inter-process communication is about: it's the family of kernel features that let one process send data to another process, or let one process tell another "something happened, react to it".
A useful mental model is that processes do not "reach into" each other’s memory. By default they are isolated. When they need to coordinate, they ask the kernel to set up a communication channel. One process writes into that channel, another process reads from it, and the kernel takes care of buffering, permissions, and waking up readers and writers when the other side becomes ready.

Pipes

The simplest channel is a pipe. A pipe is a one-way byte stream: one end is for writing, the other end is for reading. Pipes are most visible in shells, because the shell can connect programs like "A | B": program A writes bytes, program B reads those bytes as its input. Under the hood, the pipe is represented by file descriptors, so reading from a pipe uses the same read operation you would use for a file. Pipes are great for streaming data in order, but they do not preserve "message boundaries": if you write "hello" and then "world", the reader just sees a stream of bytes and may receive them in chunks that do not match your write calls.

Named pipes (FIFO)

A named pipe (also called a FIFO) is the same idea, but with a name in the filesystem so unrelated processes can rendezvous. With a regular pipe, the two endpoints usually come from a parent process that creates the pipe and then starts children that inherit the file descriptors. With a FIFO, one process can open a path for reading and another can open the same path for writing, and the kernel connects them. The FIFO file itself is not a place where the data is stored long-term; it is a meeting point the kernel uses to attach the two processes. Like pipes, FIFOs are byte streams with ordering, not message packets.

Signals

Signals are a different category: they are not a data stream, but rather a kind of notification. A signal is the kernel delivering an asynchronous "event" to a process, usually to request that it stops, pauses, continues, or handles some exceptional condition. For example, when you press Ctrl+C in a terminal, the terminal driver causes a signal to be sent to the foreground process group to request interruption. Signals are intentionally small and limited: the point is not to transfer bulk data, it is to interrupt the normal flow of execution so the program can react. Because they can arrive at almost any time, programs need to handle them carefully, and they are best used for simple control events rather than structured communication.

Sockets

Sockets are the general-purpose communication endpoints used both for local IPC and for networking. Unlike pipes, sockets are typically two-way: each side can both read and write. There are two common families. Unix domain sockets are for communication between processes on the same machine; they often appear as special filesystem entries and are commonly used by services and daemons to offer a local API. Network sockets are for communication across machines, using internet protocols like TCP or UDP. TCP sockets behave like reliable byte streams (similar to a pipe, but across the network), while UDP sockets behave more like independent packets (each send corresponds to a datagram). Sockets are a bigger toolbox than pipes: they support client/server patterns (one process listens, others connect), can carry structured connection metadata, and, with Unix domain sockets, can even support advanced features like passing file descriptors between processes.

Message queues

Instead of a continuous stream of bytes, a message queue is a kernel-managed mailbox that holds discrete messages. A sender posts a message; a receiver reads the next message. The kernel preserves message boundaries, so you can treat each message as a unit (often with optional priorities). This can make program structure simpler than a byte stream when you naturally think in "requests" and "responses". The tradeoff is that the kernel is doing more bookkeeping than it does for a plain pipe, and you are usually constrained by queue limits and message sizes.

Shared memory and semaphores

Shared memory is the fastest way for processes to exchange large amounts of data, because it avoids copying through the kernel for each transfer. The idea is simple: the kernel maps the same physical memory pages into the virtual address space of two (or more) processes. After that, both processes can read and write the same bytes directly, like they were normal memory. The kernel’s role is mostly in creating the region, mapping it, and enforcing permissions. The hard part is coordination: if two processes can write the same memory at the same time, you need rules about who writes what, and when readers can trust the contents.
That is where semaphores come in. A semaphore is a synchronization primitive: a kernel-managed counter used to control access to a shared resource or to coordinate stages of work. One common use is mutual exclusion: treating the semaphore like a lock so only one process at a time enters a critical section that touches shared state. Another use is signaling: one process "posts" to indicate that data is ready, another "waits" until that happens. Semaphores show up most often alongside shared memory, because shared memory by itself gives you speed but not safety: you still need a way to prevent races and to express ordering.