Operating System
Pipes, Sockets, Message Queues: Picking the Right IPC Isn't About Speed
Ranking IPC mechanisms by speed (shared memory fastest, sockets slowest) isn't wrong, but it's not how you'd actually pick one. The real question: what relationship do these two processes already have to each other?
Why processes need IPC at all
Processes don't share memory by default — that's the point of process isolation. Even a parent and child right after fork() have identical but separate memory images. The moment application logic splits across multiple processes — different languages, different teams, different scaling needs — those processes need an OS-provided channel to exchange anything. That channel is IPC.
A typical web app: a C/C++ web server handles raw HTTP, hands dynamic requests to a separate application server (often a scripting language, for easy text handling), which talks to a database process. Three processes, three languages, one application — none of it works without IPC gluing it together.
The mechanisms, by relationship
| Relationship | Mechanism | Why |
|---|---|---|
Parent-child, same fork() |
Pipe | Unidirectional stream (pipe() returns a read end and write end) — this is exactly what ls | grep foo uses |
| Unrelated processes, same machine, need coordination | Message queue | Mailbox abstraction — one process posts, another retrieves later, OS buffers in between |
| Client-server, possibly cross-machine | Socket | IP address + port, works identically on one machine (Unix domain sockets) or across a network |
| Same machine, need raw throughput, will handle sync yourself | Shared memory | Two processes map the same physical memory via shmget() — fastest, because there's no OS-mediated copying, but the OS gives you zero synchronization; uncoordinated writes corrupt data |
| "Something happened," no data payload | Signal | SIGINT, SIGKILL, SIGCHLD — single-bit notifications, not data. Ctrl+C is the OS delivering one specific signal, not sending a message |
The detail that actually causes production bugs: blocking
Reading from an empty pipe/socket/queue blocks by default; writing to a full one blocks too. Every mechanism has non-blocking variants. Whether a given piece of code uses the blocking or non-blocking version is usually the actual root cause when a server mysteriously "hangs" under load — if you're debugging a stuck process and IPC is involved, "is this call blocking, and on what" is the first question to ask.
Quick recall
- Pipes: parent-child via one fork(). Message queues: unrelated processes needing coordination. Sockets: client-server, possibly cross-machine. Shared memory: fastest, but you own synchronization yourself. Signals: single-bit notifications, not data.
- Picking shared memory for speed, then hand-rolling a locking scheme, is worse than picking a message queue that was already correct for the relationship
- Reading an empty channel or writing to a full one blocks by default — check blocking vs. non-blocking first when something "hangs"
Quick quiz
1. Which IPC mechanism gives zero OS-provided synchronization — fastest, but you handle locking yourself?
2. A parent and its child right after fork() want to communicate. What's the natural mechanism?
3. What's usually the real root cause when a server mysteriously 'hangs' and IPC is involved?