Communicating Processes
1. From isolation to communication: why IPC is needed
After emphasizing the value of isolation, the lecture points out that perfectly isolated processes would be too limited. Real systems need processes to cooperate, exchange information, and coordinate work. That requires inter-process communication (IPC).
Two broad mechanisms are introduced:
- message passing,
- shared memory.
2. Message passing
2.1. General idea
In message passing, the OS provides an explicit API for communication. One process sends information, and the operating system ensures that it is transferred from one protection domain to another.
The lecture stresses that message passing itself is conceptually simple, but the semantics can vary enormously. The design space includes:
- what is addressed,
- whether communication is one-way or two-way,
- Is the message buffered if it cannot be received immediately?
- whether send/receive are blocking or non-blocking,
- what the message format is,
- what guarantees are provided.
2.2. What is being addressed: process vs. mailbox
A message can be addressed in different ways.
2.2.1. Directly to a process
A system may allow a process to send directly to another process, such as a Unix signal sent to a process ID. This is simple but limited.
2.2.2. To a mailbox or endpoint
Alternatively, messages can be sent to an abstract communication object such as:
- a mailbox,
- a socket,
- an endpoint.
Sockets are a classic example: the sender does not necessarily know which process will handle the message. Instead, it sends to a named communication endpoint, and some receiver process checks that mailbox.
2.3. Buffering strategies
A major design issue is what happens if the receiver is not immediately ready.
2.3.1. Dynamically sized buffers
The most convenient design is to let the OS keep expanding buffers as needed. This is convenient for programmers but dangerous, because an unchecked producer can eventually consume all memory.
2.3.2. Fixed-size buffers
A more practical design is to have a bounded buffer. If it fills up, the producer must wait or otherwise handle the overflow. Unix pipes are an example of this style.
2.3.3. Register semantics
In embedded or cyber-physical systems, a communication channel may keep only the latest value and overwrite older ones. This is called register semantics. It is useful when old data is not interesting and only the newest reading matters. It also provides a strict bound on memory usage.
2.3.4. Rendezvous communication
Another design is rendezvous communication, where the OS does not buffer messages at all. Communication happens only when sender and receiver meet simultaneously. The OS facilitates the handoff but does not act as storage.
2.4. Blocking vs. non-blocking operations
The lecture next reviews the standard distinction:
- Blocking send/receive: the thread waits until the operation can succeed.
- Non-blocking send/receive: the operation returns immediately if it cannot proceed.
This gives many possible combinations, each with different trade-offs.
2.5. Why microkernels like synchronous rendezvous IPC
Modern microkernels, especially in the L4 tradition, often use small synchronous rendezvous messages as the basic IPC primitive. The lecture explains why this is efficient.
If the message is small enough to fit in CPU registers:
- the sender places the message in registers,
- traps into the kernel,
- if a receiver is waiting, the kernel can immediately switch to that receiver,
- and the message can effectively remain in registers across the switch.
Since the receiver is typically unblocked and run immediately, this can be even cheaper than a general context switch. That is why this design is highly attractive in performance-oriented microkernels.
2.6. Deadlock in blocking communication
The lecture gives several examples showing that blocking IPC can lead to deadlock.
For example:
- A waits to send to B,
- B waits to send to C,
- C waits to send to A.
Or similarly for receive dependencies. In both cases, no participant can make progress, and the system remains stuck forever without outside intervention.
Adding fixed-size buffers does not fundamentally solve the problem. It may make deadlock less likely or harder to trigger, but under the wrong conditions the system can still deadlock once all buffers are full. Unlimited buffers are not a general solution either, because they can exhaust memory.
2.7. Non-blocking rendezvous is nonsensical
The lecture highlights a subtle point: non-blocking rendezvous is basically a meaningless combination.
Why? Because rendezvous requires sender and receiver to meet at the same moment, but non-blocking semantics mean neither waits. In practice, this makes successful communication vanishingly unlikely or conceptually pointless.
2.8. Waiting on multiple message sources: event loops
Because waiting on only one communication source can make a process unavailable for other events, many systems support waiting on multiple sources at once.
The lecture presents this as the familiar event loop pattern, using the POSIX poll system call as an example:
- set up several file descriptors / channels,
- wait until any of them becomes ready,
- inspect which ones are ready,
- then handle events in a loop.
This design is robust and efficient, and it is the foundation of many systems such as Node.js-style event-driven frameworks.
2.9. Message formats
Messages can have many possible formats:
- a raw byte stream (e.g. pipes, TCP-style streams),
- structured packets with clear message boundaries (e.g. UDP datagrams),
- fixed-size structured bus messages (e.g. in embedded systems),
- references to protected objects such as capabilities in capability-based systems and microkernels.
The general idea is that at the lowest level messages are bytes, and richer semantics are layered on top.
2.10. Delivery guarantees
The lecture also surveys a spectrum of reliability guarantees:
- best effort delivery, where messages may be dropped, duplicated, or reordered,
- guaranteed delivery but possibly out of order, e.g., SCTP
- in-order reliable delivery such as TCP-style semantics,
- stronger all-or-nothing semantics associated with distributed transactions.
This is another large design space, and different systems choose different points based on performance and semantic needs.
2.11. Advantages of message passing
The lecture identifies several major strengths of message passing:
- communication is explicit,
- it is easy to see when communication happens,
- it is easy to know what data is exchanged,
- systems can interpose checks, validation, proxies, logging, and load balancing,
- untrusted clients can be handled safely at message boundaries.
This makes message-based systems easier to audit and often easier to reason about at a high level.
2.12. Disadvantages of message passing
At the same time, message passing has costs:
- serialization / marshalling and unmarshalling overhead,
- copying overhead,
- complexity of debugging distributed or partially distributed behavior,
- extra error handling throughout the code base.
Thus, if communication is extremely frequent or data-heavy, pure message passing may be too expensive.