A web server receives unpredictable bursts of image-processing requests that take 200ms each to handle. The processing is done by a separate worker process. Should the server use a pipe or a message queue to send jobs to the worker, and why?
AA pipe — it is faster and lower overhead than a message queue
BA message queue — the server can enqueue bursts immediately without blocking, and the worker processes jobs at its own pace
CA pipe — it supports priority ordering, so urgent requests can jump the queue
DA message queue — it uses shared memory internally so it is faster for large image data
The key advantage of message queues is temporal decoupling: the sender (web server) deposits a message and continues executing immediately, without waiting for the receiver (worker) to consume it. During a burst, messages accumulate in the queue rather than blocking the server. A pipe would block the server once the pipe buffer fills, degrading responsiveness. Message queues also support typed/priority messages (pipes carry only undifferentiated byte streams). The lower throughput compared to shared memory is acceptable here since image jobs are discrete, moderate-frequency events.
Question 2 Multiple Choice
What distinguishes message queues from pipes as IPC mechanisms?
APipes support multiple senders and receivers; message queues support only one of each
BMessage queues carry typed, discrete messages that can be selectively retrieved; pipes carry an undifferentiated byte stream that must be read in strict FIFO order
CMessage queues require both processes to run simultaneously; pipes do not
DPipes are managed by the kernel; message queues are managed by user-space libraries
Pipes stream raw bytes — the receiver must process data in exactly the order it was sent, with no structure beyond byte position. Message queues carry discrete messages with type fields, allowing the receiver to selectively retrieve messages by type (e.g., process priority messages before routine ones). This makes message queues suitable for multi-channel communication within a single queue. The opposite is true for synchronization: pipes block the writer when full and the reader when empty; message queues asynchronously decouple sender and receiver.
Question 3 True / False
When a process sends a message to a message queue, it blocks until the receiving process retrieves the message.
TTrue
FFalse
Answer: False
Temporal decoupling is the defining feature of message queues. The sender deposits the message into the kernel-managed queue and returns immediately — the receiver does not need to be running or even to exist at the moment of sending. The sender only blocks if the queue is full (at its configured capacity limit), at which point it waits for the receiver to drain some messages. This asynchronous behavior is exactly what makes message queues useful for handling rate mismatches between producers and consumers.
Question 4 True / False
Message queue resources in the POSIX/System V kernel persist until they are explicitly removed, even if all processes that used the queue have terminated.
TTrue
FFalse
Answer: True
This is a deliberate design difference from pipes. Pipes are reference-counted: when both the read and write ends are closed, the kernel cleans up automatically. Message queues have kernel persistence — they survive process termination and remain available (with any unread messages) until a process explicitly deletes them (via mq_unlink or msgctl IPC_RMID). This allows a new process to start, find the existing queue by name or key, and read messages left by a process that has since exited. The tradeoff is that abandoned queues accumulate kernel resources if not cleaned up.
Question 5 Short Answer
Explain temporal decoupling in message queues — what it means and why it matters for system design.
Think about your answer, then reveal below.
Model answer: Temporal decoupling means the sender and receiver do not need to be running simultaneously or processing at the same rate. The sender deposits a message into the kernel queue and continues executing; the receiver retrieves messages at its own pace. This matters because it absorbs rate mismatches: if the receiver is temporarily overloaded, messages buffer in the queue rather than blocking or crashing the sender. It also simplifies restart logic — if the receiver crashes and restarts, unprocessed messages remain in the queue. This pattern is foundational to resilient system design and is the precursor to distributed message brokers like RabbitMQ and Kafka.
Without temporal decoupling (as with synchronous IPC like pipes or direct function calls), sender and receiver must be synchronized — the sender waits for the receiver, creating tight coupling. Temporal decoupling trades a small amount of latency and memory (the queue buffer) for resilience and flexibility. It enables the sender to continue serving requests during receiver downtime, and allows horizontal scaling by adding multiple consumers reading from the same queue.