AlgoInsight

Free lesson · Operating Systems

Why Processes Need To Communicate

Easy11 min readoperating-systemsosconcurrency

Isolation Is Not Enough

In Module 01, you learned that processes are isolated. Each process has its own virtual address space. One process cannot read or write another process's memory. This isolation is a safety feature - it prevents one buggy process from corrupting another, and it is the foundation of OS security and stability.

But isolation creates a problem: real systems are not built from a single process doing everything. They are built from multiple processes that need to work together. A web server needs to hand requests to a database process. A shell needs to send output from one command to the input of another. A browser's rendering process needs to receive network data from the network process. A microservice needs to call another microservice. A container needs to communicate with the host.

If processes are isolated - if they cannot see each other's memory - how do they share data, coordinate their actions, and communicate? The answer is Inter-Process Communication (IPC) - a set of mechanisms the operating system provides that allow processes to exchange data and synchronize their behavior despite their address space isolation.

Understanding IPC matters for interviews in two ways. First, specific IPC mechanisms (pipes, sockets, shared memory) come up directly in questions like "how does ls | grep work?" or "how do microservices communicate?" Second, IPC tradeoffs (speed versus safety, shared memory versus message passing) are system design fundamentals. When you choose between a monolith and microservices, you are implicitly choosing an IPC model - and you should be able to explain the consequences.

The Two Fundamental Models

Every IPC mechanism falls into one of two fundamental models: shared memory and message passing. Understanding these two models - and when to use each - is the conceptual foundation for everything in this module.

Shared Memory

In the shared memory model, two or more processes map the same region of physical memory into their respective virtual address spaces. Once the shared region is set up (via a system call), processes can read and write to it directly - no kernel involvement for each read or write. The data is just there, in memory that both processes can see.

Shared memory is the fastest IPC mechanism because, after the initial setup, communication is just memory reads and writes - no system calls, no kernel overhead, no data copying. A process writes a value to the shared region, and the other process reads it directly. The cost is a memory access - nanoseconds.

But shared memory is also the most dangerous IPC mechanism. Because both processes can read and write the same memory simultaneously, all the problems of concurrent access apply - race conditions, data corruption, and the need for synchronization (mutexes, semaphores). Shared memory gives you raw speed but transfers the responsibility for correctness to the programmer. If you get the synchronization wrong, you get corrupted data. The OS does not protect you.

Message Passing

In the message passing model, processes communicate by sending and receiving messages through a channel managed by the kernel. One process calls send(message), the kernel copies the message to an internal buffer, and the other process calls receive() to get the message from the buffer.

Message passing is slower than shared memory because every send and receive involves a system call and at least one data copy (from the sender's address space to the kernel buffer, and from the kernel buffer to the receiver's address space). On some systems, the kernel can optimize this to a single copy, but there is always kernel involvement.

But message passing is safer than shared memory. The channel is sequential - messages arrive in order, and only one process reads each message. There is no shared state to corrupt, no race conditions on the communication channel itself, and no need for explicit synchronization. The kernel manages the buffer and ensures data integrity.

graph LR
    subgraph SM["Shared Memory Model"]
        PA["Process A"] -->|"writes to"| SHM["Shared<br/>Memory<br/>Region"]
        PB["Process B"] -->|"reads from"| SHM
        SHM -->|"reads from"| PA
        SHM -->|"writes to"| PB
    end
    
    subgraph MP["Message Passing Model"]
        PC["Process C"] -->|"send(msg)"| KERNEL["Kernel<br/>Message<br/>Buffer"]
        KERNEL -->|"receive()"| PD["Process D"]
    end
This diagram contrasts the two IPC models. In the shared memory model, Process A and Process B both read from and write to a shared memory region directly, with no kernel involvement after setup. In the message passing model, Process C sends a message to a kernel-managed buffer, and Process D receives the message from that buffer. The kernel mediates every communication in message passing.

Comparing the Two Models

Property Shared Memory Message Passing
Speed Very fast (memory access) Slower (system calls + copies)
Kernel involvement Only during setup Every send/receive
Synchronization Programmer's responsibility Built into the channel
Data copying Zero copies after setup At least one copy per message
Complexity High (need explicit sync) Lower (sequential channel)
Scalability Limited to same machine Can work across machines (sockets)
Safety Low (race conditions possible) High (kernel-managed)
Best for High-throughput same-machine Distributed systems, safety-critical

The choice between shared memory and message passing is one of the most fundamental design decisions in systems programming, and it appears at every level of system architecture.

Within a single machine: Shared memory is used for high-performance communication between processes that need to exchange large amounts of data quickly - database systems, multimedia pipelines, scientific computing. Message passing (pipes, sockets, message queues) is used when safety and simplicity are more important than raw speed - shell pipelines, client-server communication, worker process coordination.

Across machines: Message passing is the only option. You cannot share physical memory between two machines (ignoring specialized hardware like RDMA). Network sockets, gRPC, message brokers (Kafka, RabbitMQ), and HTTP APIs are all message-passing mechanisms. The entire microservices architecture is built on message passing between processes running on different machines.

Interview: "What are the two IPC models?"

"The two fundamental IPC models are shared memory and message passing. In shared memory, processes map the same physical memory into their address spaces and communicate by reading and writing directly - very fast (no system calls after setup) but requires synchronization to avoid race conditions. In message passing, processes send and receive messages through a kernel-managed channel - slower (involves system calls and data copies) but safer because the kernel ensures data integrity and ordering.

Shared memory is best for high-throughput communication on the same machine. Message passing is best for distributed systems and for situations where safety matters more than speed. Microservices, by definition, use message passing - they communicate via network sockets, HTTP, or message brokers."

IPC in the Real World

IPC mechanisms are not abstract concepts - they are the plumbing that real systems are built on. Here are examples you can reference in interviews:

Shell pipelines. When you type ls | grep txt | sort, three processes are created. The output of ls is piped to the input of grep, and the output of grep is piped to sort. This is message passing using pipes - the kernel manages a buffer between each pair of processes. Topic 02 covers pipes in detail.

Web server to database. When a web server process sends a SQL query to a PostgreSQL process, it uses a socket - either a Unix domain socket (for same-machine communication) or a TCP socket (for network communication). This is message passing. The query and result are serialized into bytes, sent through the socket, and deserialized on the other end. Topic 05 covers sockets.

Redis's persistence mechanism. Redis uses fork() to create a child process that writes the in-memory dataset to disk. The child inherits a copy-on-write snapshot of the parent's memory. This is not traditional IPC - the child gets the data through the fork mechanism rather than an explicit communication channel. But it illustrates how process creation and memory sharing interact.

Chrome's multi-process architecture. Chrome runs each tab as a separate process. The browser process (which handles the UI) communicates with renderer processes (which handle web pages) using IPC channels - message-passing mechanisms specific to Chrome's architecture. These channels send serialized messages between processes, allowing the UI to display content from the renderer without sharing memory directly.

Kafka and message brokers. Apache Kafka is a distributed message-passing system. Producer processes send messages to Kafka, and consumer processes receive them. Kafka manages the message buffer (stored on disk), handles ordering (within partitions), and provides durability (messages survive process and machine failures). This is message passing scaled to the distributed system level - the same conceptual model as a Unix pipe, but spanning many machines.

Shared memory in databases. PostgreSQL uses shared memory for its buffer pool - the in-memory cache of disk pages. All PostgreSQL backend processes (one per client connection) access the same shared memory region to read and write cached pages. This requires careful synchronization (using lightweight locks called LWLocks) to prevent corruption. The shared buffer pool is why PostgreSQL can serve many clients efficiently without each one reading from disk independently.

Synchronous vs Asynchronous Communication

Beyond the shared memory vs message passing distinction, IPC mechanisms also differ in whether communication is synchronous (blocking) or asynchronous (non-blocking).

Synchronous IPC. The sender blocks until the receiver gets the message, or the receiver blocks until a message is available. A function call across a Unix pipe is synchronous by default - write() may block if the pipe buffer is full, and read() blocks if the pipe buffer is empty. Synchronous communication is simple to reason about (the sender knows the message was delivered) but can cause performance problems if one side is slower than the other.

Asynchronous IPC. The sender does not wait for the receiver. It places the message in a buffer and continues immediately. If the buffer is full, the sender either drops the message, blocks, or gets an error - depending on the configuration. Message queues are often asynchronous - the producer adds messages to the queue and continues without waiting for a consumer to process them. This decouples the sender and receiver in time, which is essential for building scalable distributed systems.

The synchronous/asynchronous choice has major implications for system design. Synchronous communication creates tight coupling - if the receiver is slow or down, the sender is affected. Asynchronous communication creates loose coupling - the sender and receiver operate independently, with the buffer absorbing temporary speed differences. This is why message brokers like Kafka and RabbitMQ exist: they provide asynchronous, buffered, durable message passing between services that may operate at different speeds.

IPC and the Producer-Consumer Pattern

The most common IPC pattern is producer-consumer: one process produces data, another consumes it. This pattern appears everywhere in systems programming:

Shell pipelines are producer-consumer: ls produces filenames, grep consumes them and produces matches, sort consumes matches and produces sorted output. Each stage is both a consumer (of its input) and a producer (of its output).

Web server request handling is producer-consumer: the network listener process accepts connections and produces work items, and worker processes consume work items and produce responses.

Logging systems are producer-consumer: application processes produce log entries, and a log collector process consumes them and writes them to disk or sends them to a centralized logging service.

Event streaming is producer-consumer at scale: Kafka producers write events to topics, and Kafka consumers read events from topics. The broker (Kafka) manages the buffer between producers and consumers, providing durability, ordering, and replay.

The producer-consumer pattern requires coordination: what happens when the producer is faster than the consumer? The buffer fills up. What happens when the consumer is faster? It waits for data. This coordination is handled differently by each IPC mechanism - pipes use a kernel buffer with blocking reads and writes, message queues use configurable buffer sizes, and shared memory requires explicit synchronization with semaphores or condition variables. Module 05 (Process Synchronization) covers the producer-consumer synchronization problem in depth.

IPC Across the System Design Spectrum

Understanding where IPC fits in the system design spectrum helps you make connections in interviews:

Within a single process (threads): Communication is via shared memory - threads share the heap. No IPC mechanism needed, but synchronization (mutexes, condition variables) is required. This is the fastest communication model.

Between processes on the same machine: IPC mechanisms - pipes, Unix domain sockets, shared memory, message queues. These are the focus of this module. Performance is between thread communication and network communication.

Between processes on different machines: Network protocols - TCP sockets, HTTP, gRPC, message brokers. This is the domain of distributed systems. Performance is limited by network latency and bandwidth.

Each level adds latency and overhead but also adds isolation and independence. Moving from threads to processes adds address space isolation. Moving from same-machine to cross-machine adds network independence (processes can fail independently). These tradeoffs are at the heart of the monolith-versus-microservices debate in system design.

graph TD
    subgraph IPC_SPECTRUM["IPC Mechanisms Spectrum"]
        FAST["Fastest<br/>────<br/>Shared Memory"]
        MED_FAST["Fast<br/>────<br/>Unix Domain Sockets"]
        MED["Medium<br/>────<br/>Pipes, Message Queues"]
        SLOW["Slower<br/>────<br/>TCP Sockets"]
        SLOWEST["Slowest<br/>────<br/>HTTP/gRPC<br/>(network + serialization)"]
    end
    
    FAST --> MED_FAST --> MED --> SLOW --> SLOWEST
    
    subgraph SAFETY["Safety and Simplicity"]
        S1["Least safe<br/>(manual sync)"]
        S5["Most safe<br/>(full protocol)"]
    end
    
    FAST -.-> S1
    SLOWEST -.-> S5
This diagram shows IPC mechanisms on a spectrum from fastest to slowest. Shared memory is fastest, followed by Unix domain sockets, then pipes and message queues, then TCP sockets, and finally HTTP or gRPC which are the slowest due to network overhead and serialization. The safety axis runs inversely - shared memory is the least safe (requires manual synchronization) while HTTP and gRPC are the most safe (full protocol handling). Faster mechanisms require more manual safety management.

What This Module Covers

The remaining topics in this module cover the specific IPC mechanisms in order of increasing complexity and scope:

Topic 02 covers pipes - the simplest IPC mechanism, used in shell pipelines and parent-child communication. Topic 03 covers message queues - a more structured message-passing mechanism with message types and priorities. Topic 04 covers shared memory - the fastest IPC mechanism and the one that requires the most careful programming. Topic 05 covers sockets - the mechanism that powers network communication and same-machine IPC for databases and microservices. Topic 06 covers signals - a lightweight notification mechanism that is conceptually different from data-transfer IPC.

Each topic explains the mechanism, shows how it works with code examples, and connects it to real-world use cases you can reference in interviews.

Tip: For system design interviews: When the interviewer asks how components of your system communicate, the IPC model is part of the answer. "The web server communicates with the database via Unix domain sockets" is more specific and impressive than "the web server talks to the database." Naming the IPC mechanism shows OS-level understanding.

Exercise: Starter: List three things your computer is doing right now that involve IPC. For each one, identify which processes are communicating and whether they are likely using shared memory or message passing.

Intermediate: Consider a system with a producer process that generates 1 million events per second and a consumer process that processes them. Compare the performance characteristics of shared memory versus Unix domain sockets for this workload. Which would you choose? What synchronization would you need for shared memory?

Advanced: Research how gRPC works underneath. What IPC mechanisms does it use? How does it serialize data (Protocol Buffers)? What transport protocol does it use (HTTP/2)? How does this compare in performance to Unix domain sockets or shared memory? When would you choose gRPC over simpler IPC mechanisms?

Back to Operating Systems