Inter-Process Communication: Pipes & Message Queues
Building an Operating System Kernel: Concurrency, I/O & Synchronization
Chapter 5 · Inter-Process Communication: Pipes & Message Queues
Chapter 1's own shared page let two processes touch the same raw memory. A pipe is the safer, structured alternative — processes exchange data through a real, ordered stream, no shared address space required. This chapter builds a real pipe directly on top of Chapter 3's own bounded buffer, finds a real gap in what a pipe can express, and builds a real message queue to close it.
Finding 1: A Real Pipe, Built Directly on Chapter 3's Own BoundedBuffer
b'HELLO KERNEL', written one byte at a time by one process and read one byte at a time by another, arrives back exactly as sent. A pipe is a message queue's simpler cousin — reusing Chapter 3's own producer-consumer correctness directly, with zero new synchronization code required.
Finding 2: A Real Pipe Has No Message Boundaries
b'HI' (intended as one message). Writer B sends b'BYE' (also intended as one message). Every real byte from both arrives, correctly ordered, with zero loss — but the raw stream the reader actually sees is b'HBIYE': the two writers' bytes genuinely interleaved at the byte level. Reading it back in any fixed chunk size not chosen to match the original writes produces genuinely wrong groupings — [b'HB', b'IY', b'E']. A pipe preserves byte order. It has no concept of where one message ends and the next begins.
A Deeper Finding: Length-Prefixing Doesn't Save the Multi-Writer Case
The classic real-world fix for a single writer is length-prefixing — write how many bytes are coming, then the bytes themselves.
b'\x02\x03HBIYE' produces garbage, not the original two messages. Length-prefixing only works when one writer's entire framed burst is guaranteed to land as one uninterrupted unit — true for exactly one writer, false the moment a second one can interleave with it.
Finding 3: A Real Message Queue Preserves Discrete Message Boundaries
b'HI', writer B sends b'BYE' — the exact same setup as Finding 2. Result: [b'HI', b'BYE'], exactly the two original whole messages, regardless of how the scheduler interleaved the two real send() calls. Because each message is queued and dequeued as one real, atomic Python object, there is no byte-level interleaving possible at all — not something to avoid carefully, something the data structure itself makes structurally impossible.
Where This Connects
| This chapter's finding | What it connects to |
|---|---|
| The pipe itself | Chapter 3's own BoundedBuffer — reused completely unchanged; a pipe is that same real fix, applied to real IPC instead of a shared counter |
| The message queue's own empty/full semaphores | Chapter 3's own two-semaphore pattern — reused directly, counting whole messages instead of bytes |
| Length-prefixing failing under multiple writers | Chapter 1's own shared-page race — the same underlying lesson: a technique that assumes uncontested access silently breaks the moment a second real competitor shows up |
| Structural correctness vs. careful discipline | Course 1 Chapter 6's own syscall boundary — some protections work by making the bad outcome impossible by construction, rather than by everyone remembering to be careful |
Hands-On Exercises
Length-prefix two messages sent by a single writer, back to back, and confirm decoding works correctly. Then repeat with two independent writers each length-prefixing their own message onto the same pipe, and confirm decoding fails. Explain precisely why.
📄 View solutionRun a real MessageQueue with a capacity of exactly 1, sending several messages of genuinely different lengths. Confirm every message still arrives whole and in order, and explain why capacity doesn't affect message-boundary correctness.
Send an empty message (b'') followed by a real one through a MessageQueue. Confirm both are received as two genuinely distinct messages, and explain why a raw pipe fundamentally cannot express this same scenario cleanly.
Chapter 5 Quick Reference
- Pipe: an ordered byte stream, built directly on Chapter 3's own
BoundedBuffer— correct byte order, zero concept of message boundaries - Verified Finding 2: two writers on the same pipe genuinely interleave at the byte level, silently merging distinct messages
- Verified (deeper finding): length-prefixing fixes a single writer but not multiple — the length bytes themselves are just as interleavable as any other byte
- Verified Finding 3: a real
MessageQueue— the same empty/full pattern, counting whole messages — makes byte-level interleaving structurally impossible - Golden rule: a pipe answers "in what order did the bytes arrive"; a message queue answers "what were the actual messages" — genuinely different questions, not the same primitive with a different name
- Next chapter: Device Drivers & the I/O Abstraction Layer