Interview questions

Interprocess Communication Linux Interview: The IPC Playbook

August 28, 2025Updated May 28, 202623 min read
Why Is Interprocess Communication In Linux A Must-know For Your Next Technical Interview

A Linux IPC interview playbook with the primitives interviewers actually ask about: pipes, FIFOs, shared memory, message queues, semaphores, and Unix domain.

Knowing the names of IPC primitives is not the same as knowing which one to choose when an interviewer puts you on the spot. The interprocess communication Linux interview question that trips up most candidates isn't "what is a pipe?" — it's the follow-up: "why not shared memory here?" or "what happens when the process dies and nobody cleaned up?" That's the gap this playbook closes. Not definitions, but decisions — the mechanism, the syscall, the tradeoff, and how to say it in thirty seconds without sounding like you're reciting a textbook.

Most candidates who study IPC can name five primitives. Fewer can explain why they'd reach for a FIFO instead of a pipe, or why shared memory without a semaphore is just a fast way to corrupt state. The strongest interview answers move through a simple sequence: name the mechanism, name the API, name the synchronization cost, and name one failure mode. That sequence is what this guide teaches.

What Linux IPC Really Tests in an Interview

The Interviewer Is Not Asking for Definitions

Linux interprocess communication exists because processes have separate address spaces by design. The kernel enforces that boundary, and IPC is the set of mechanisms for crossing it deliberately. Interviewers already know you know this. What they're actually probing is whether you can move from "I know what shared memory is" to "here's why I'd use it for this specific constraint and not for that one."

The real test is mechanism selection under constraint. Latency-sensitive? That changes the answer. Unrelated processes that don't share a parent? That changes it again. One-way stream versus message boundaries? Different primitive entirely. The interviewer is simulating the engineering decision you'd make on the job, not running a vocabulary quiz.

Why Textbook Answers Fall Apart Under Follow-Up

A memorized definition holds up for exactly one question. Say you answer "shared memory is a region of memory mapped into two or more process address spaces." The interviewer nods and asks: "What about synchronization?" If your mental model is purely definitional, you have nothing to say — because the definition doesn't include the part where two writers corrupt each other's data without a semaphore.

This is the structural failure of fact-based prep for systems interviews. The facts are necessary but not sufficient. The interviewer is testing whether you understand the runtime behavior — what happens at open, at write, at close, and at process exit. A candidate who can walk through that lifecycle sounds like someone who has actually used the mechanism, not just read about it.

What This Looks Like in Practice

The prompt "how would you pass data between two Linux processes?" has a weak answer and a strong one.

Weak: "You could use pipes or shared memory or sockets."

Strong: "If these are related processes — parent and child — I'd start with `pipe()`, since the file descriptor is inherited across `fork()` and the kernel handles buffering. If they're unrelated, I'd use a FIFO created with `mkfifo()` so they can meet in the filesystem. If throughput is the bottleneck and I need zero-copy, I'd move to shared memory with `shmget()` and `shmat()`, but then I'd need a semaphore for coordination. The choice depends on whether the processes share ancestry, what the throughput requirement is, and how much synchronization complexity I'm willing to own."

That answer names the mechanism, the syscall, and the tradeoff. It's the difference between sounding like someone who read the Linux man pages and someone who has made this decision under real constraints.

Choose the IPC Mechanism That Fits the Job

Start From the Constraint, Not the Mechanism

The instinct is to memorize which primitive does what and then pattern-match to a question. That breaks down the moment the interviewer adds a constraint you didn't anticipate. The better approach is to work backward from the problem.

Ask yourself four questions before naming a mechanism:

  • Are the processes related? Parent-child relationships mean inherited file descriptors, which makes `pipe()` the simplest path.
  • Is throughput the bottleneck? If you're moving large amounts of data and latency matters, shared memory eliminates the copy cost that pipes and message queues pay.
  • Do you need message boundaries? Pipes give you a byte stream — no boundaries. Message queues preserve them.
  • Who owns cleanup? Shared memory and System V IPC objects persist after the process exits unless explicitly removed. That's a production concern and an interview trap.

Starting from constraint rather than mechanism is what separates a senior-sounding answer from a junior one. It shows you're thinking about the system, not just the API.

Pipes and FIFOs Are for Simple Streams, Not Cleverness

IPC primitives in Linux span a wide capability range, but pipes and FIFOs are the right answer more often than people admit. When all you need is a one-way byte stream between two processes, a pipe is the cleanest solution — minimal setup, kernel-managed buffering, automatic EOF when the write end closes. Reaching for shared memory when a pipe would do is not a sign of sophistication; it's a sign of over-engineering.

FIFOs extend the pipe model to unrelated processes by giving the channel a name in the filesystem. That's the only structural difference, but it matters: two processes that don't share a parent can open the same FIFO path and communicate without any prior coordination beyond agreeing on the path.

What This Looks Like in Practice

Say you're building a parent process that spawns a worker and feeds it log lines to process. The processes share ancestry, the data is a simple stream, and you don't need bidirectional communication. `pipe()` is the answer — it's two file descriptors, one for reading and one for writing, inherited across `fork()`, and it requires zero filesystem state. If you later need two unrelated shell sessions to talk to the same log processor, you'd replace `pipe()` with `mkfifo()` and give the channel a path both sides can open. If you need bidirectional communication between related processes, `socketpair()` gives you that in one call. The Linux man page for pipe(2) lays out the exact semantics — it's worth reading before any systems interview.

Pipes vs FIFOs: Pick the Boring Thing When Boring Is Enough

A Pipe Is Private; a FIFO Is Named

A pipe is anonymous — it exists only as two file descriptors in the creating process, inherited by children after `fork()`. Nothing outside that process family can open it, because there's nothing to open. A FIFO (named pipe) is created with `mkfifo()` and appears as a special file in the filesystem. Any process that knows the path can open it, subject to permissions.

Interviewers care about this distinction because it maps directly to process relationships. If you say "I'd use a pipe," the interviewer expects you to know that only processes sharing ancestry can use it. If you say "I'd use a FIFO," you're implying the processes don't share a parent — and the interviewer may follow up on the filesystem path, permissions, and cleanup.

The Part People Forget Is Lifecycle

A pipe's lifecycle is tied to its file descriptors. When all write ends are closed, readers get EOF. When all read ends are closed, writers get SIGPIPE. The kernel handles the buffer; you handle the file descriptors. Clean and predictable.

FIFOs are stickier. The file persists in the filesystem after both sides close. Opening a FIFO blocks by default until both a reader and a writer have opened it — which catches people off guard when they test with a single process. The POSIX FIFO specification defines this blocking behavior explicitly. In a real system, you need to think about who creates the FIFO, who removes it, and what happens if one side crashes before the other opens it.

What This Looks Like in Practice

Parent spawns worker, feeds it commands, reads results: `pipe()`, two pairs, one for each direction — or `socketpair()` if you want a cleaner bidirectional API. Two unrelated shell sessions exchanging data through a shared channel: `mkfifo /tmp/channel`, one session writes, the other reads, and you delete the file when you're done. The hinge is ancestry. If the processes already share a parent, pipes are simpler. If they don't, FIFOs or Unix domain sockets are the practical options. Verifying blocking behavior is straightforward: open a terminal, run `cat /tmp/channel`, and watch it block until a writer appears. That five-minute shell demo is worth more than reading the definition three times.

Shared Memory Wins on Speed, Then Loses on Coordination

Why Shared Memory Looks Magical Until You Add Writers

Shared memory is the fastest IPC mechanism in Linux because it eliminates the copy. With pipes or message queues, data travels from the sender's buffer into the kernel and then into the receiver's buffer — two copies. With shared memory, both processes map the same physical pages into their address spaces and read or write directly. For high-throughput producer-consumer scenarios, this matters enormously.

The appeal is real. The price is that the kernel stops helping you the moment data lives in a shared region. With pipes, the kernel serializes writes and manages the buffer. With shared memory, you own all of that. Two processes writing to the same region without coordination will corrupt each other's data silently — no error, no signal, just wrong output.

If You Don't Mention Semaphores, You Haven't Finished the Answer

Any shared memory answer that doesn't include synchronization is incomplete. Interviewers know this and will wait for you to bring it up. If you don't, they'll ask "what about concurrent writes?" and you'll be playing catch-up.

The standard pairing is shared memory plus semaphores. A semaphore controls access to the shared region — one process acquires it, writes, and releases it; the other waits, acquires, reads, and releases. Without that ordering, you have a race condition: two processes enter the critical section simultaneously, one overwrites the other's data, and the bug only appears under load. That's the interview trap, and naming it proactively is what makes you sound like someone who has been burned by it.

What This Looks Like in Practice

The prompt "when would you use shared memory over pipes?" has a clean answer: when you're moving large buffers between processes and the copy cost is measurable. A video processing pipeline, a high-frequency trading system reading market data, a game engine sharing a frame buffer between a render process and a display process — these are cases where zero-copy matters.

The API sequence: `shmget()` to create or attach to a shared memory segment, `shmat()` to map it into the process's address space, `shmdt()` to detach, and `shmctl()` with `IPC_RMID` to remove it. For coordination: `semget()` to create a semaphore set, `semop()` to perform wait and signal operations. The Linux man page for shmget(2) and the semop(2) page are the canonical references. Name those calls in an interview and you immediately sound like someone who has actually written this code.

Message Queues Are for Decoupling, Not Raw Speed

When You Need Messages, Not a Stream

Message queues solve a different problem from pipes. A pipe gives you a byte stream — the reader gets bytes in the order they were written, with no concept of where one message ends and another begins. A message queue preserves message boundaries. Each `msgsnd()` call sends a discrete unit; each `msgrcv()` call receives exactly one. That boundary preservation matters when your protocol has variable-length messages or when the receiver needs to process units, not bytes.

The other thing message queues give you is decoupling. The sender doesn't need to know who the receiver is, whether the receiver is running, or how fast the receiver is processing. It puts a message in the queue and moves on. The receiver picks it up when it's ready. That producer-consumer separation is the architectural reason to choose message queues over pipes, even when raw throughput isn't the bottleneck.

The Cost of Convenience Is Queue Management

Message queues are not free. The kernel imposes limits on queue size and message count — `MSGMAX` for maximum message size, `MSGMNB` for maximum bytes per queue. If the queue fills up, `msgsnd()` blocks by default. If the receiver crashes and nobody drains the queue, it backs up. System V message queues persist after the creating process exits, just like shared memory — they don't go away until someone calls `msgctl()` with `IPC_RMID` or runs `ipcrm -q`.

These operational details are where interview answers usually run out of steam. Naming the API is table stakes. Knowing what happens when the queue fills, who owns cleanup, and how to inspect queue state with `ipcs` is what separates a production-ready answer from a textbook one.

What This Looks Like in Practice

A daemon receives job requests from multiple producers and dispatches them to worker processes. Each job is a discrete unit — a file path, a task ID, a command. Message queues fit this exactly: producers call `msgsnd()` with a typed message, the dispatcher calls `msgrcv()` with a type filter to pull the next job, and the queue handles the buffering and ordering. The API: `msgget()` to create or open the queue, `msgsnd()` to enqueue, `msgrcv()` to dequeue, `msgctl()` to inspect or remove. This is a common pattern in older Unix daemons and maintenance scripts, and naming it with the right syscalls signals that you understand System V IPC as a real production tool, not just a concept.

Semaphores Are the Answer When the Real Problem Is Coordination

Semaphores Don't Move Data; They Control Access

This is the boundary that candidates most often blur. A semaphore is not a data transport mechanism. It moves nothing. Its job is to control which process is allowed to enter a critical section at any given moment. You pair it with shared memory or any other shared resource — the semaphore is the lock, not the channel.

Synchronization and semaphores go together in Linux IPC the way a mutex goes with a shared variable in threaded code. The shared memory region is the variable; the semaphore is the mutex. Without the semaphore, the shared region is a race condition waiting to happen. With it, you have a controlled access protocol that the kernel enforces through atomic wait and signal operations.

Race Conditions and Deadlock Are the Interview Traps

The moment you mention shared memory in an interview, you've invited questions about ordering. The two failure modes interviewers test for are race conditions and deadlock.

A race condition happens when two processes access a shared resource concurrently without coordination and the outcome depends on timing. The result is data corruption that's hard to reproduce because it only manifests under specific scheduling conditions. Naming this proactively — "without a semaphore, two writers can corrupt the same region" — shows you understand why coordination is mandatory, not optional.

Deadlock happens when two processes each hold a resource the other needs and neither can proceed. In an IPC context, this typically means process A holds semaphore 1 and waits for semaphore 2, while process B holds semaphore 2 and waits for semaphore 1. The Linux man page for semop(2) documents the `SEM_UNDO` flag, which automatically reverses semaphore operations if the process exits — a useful tool for avoiding leaked locks that would block everyone else forever.

What This Looks Like in Practice

One writer, one reader, one shared memory region. The writer calls `semop()` to decrement the semaphore (wait), writes its data, then calls `semop()` again to increment (signal). The reader waits on the same semaphore before reading. The API: `semget()` to create or open the semaphore set, `semop()` to perform atomic wait and signal, `semctl()` to initialize or remove. In an interview, saying "I'd initialize the semaphore to 1 so the first operation succeeds, use `semop()` with `sem_op = -1` to acquire and `+1` to release, and set `SEM_UNDO` so a crash doesn't leave the semaphore locked" is a complete, production-aware answer.

Unix Domain Sockets Are the IPC Primitive Interviewers Forget to Ask About

They Give You Sockets Without Pretending the Network Exists

Unix domain sockets use the socket API — `socket()`, `bind()`, `connect()`, `send()`, `recv()` — but communicate through the kernel's local IPC path rather than a network stack. No TCP overhead, no IP routing, no loopback. Just local, fast, bidirectional communication with a socket-shaped interface that backend engineers already know.

This matters in an interview because it signals production awareness. A candidate who mentions Unix domain sockets is implicitly saying: "I know that `localhost` TCP has overhead, I know that pipes are one-directional, and I know there's a local IPC mechanism that gives me the best of both." That's a more sophisticated answer than the standard pipe/shared-memory binary.

Why This Is Often the Most Practical Production Answer

`socketpair()` creates a connected pair of Unix domain sockets in a single call — no binding, no listening, no accepting. It's the bidirectional equivalent of `pipe()` for related processes. For unrelated processes, you create a Unix domain socket, bind it to a filesystem path, and let clients connect to it. You can also pass file descriptors through a Unix domain socket using `sendmsg()` with `SCM_RIGHTS` — a capability that pipes and shared memory simply don't have.

In production, this pattern shows up everywhere: `systemd` communicates with services through Unix domain sockets, `nginx` worker processes talk to the master through them, and most local daemon-control protocols use them because they're fast, bidirectional, and familiar.

What This Looks Like in Practice

A service process and its supervisor need to exchange control messages: start, stop, reload, status. A Unix domain socket bound to `/run/myservice.sock` lets the supervisor connect, send a command, and read the response — all through the same `send()`/`recv()` calls it would use for a network socket, but with zero network overhead. `socketpair()` handles the same pattern for parent-child communication without touching the filesystem. The distinction from a network socket is the `AF_UNIX` address family in the `socket()` call. The distinction from a pipe is bidirectionality and the socket API. Name those two distinctions in an interview and you've demonstrated more Linux IPC depth than most candidates show.

Give the 30-Second Answer Without Sounding Rehearsed

Lead With the Choice, Then Defend It

IPC interview questions reward a specific answer order: mechanism first, justification second, tradeoff third. Not "well, there are several options" — that's a stall. Not a five-paragraph tour of every primitive — that's noise. The interviewer wants to see you make a decision and own it.

The pattern: "I'd use [mechanism] because [constraint]. The tradeoff is [cost], which I'd handle by [mitigation]." That's the whole structure. It's direct, it's defensible, and it leaves room for the follow-up without collapsing under it.

What This Looks Like in Practice

Five model answers for common IPC interview questions:

"When would you use shared memory over pipes?" — "When I'm moving large buffers and the copy cost is measurable. Pipes pay two copies through the kernel; shared memory pays zero. The tradeoff is synchronization — I'd use a semaphore to coordinate access and `SEM_UNDO` to handle crashes."

"Pipes vs FIFOs — when do you use each?" — "Pipes for related processes that share a parent via `fork()`. FIFOs when the processes are unrelated and need to meet in the filesystem. The FIFO persists after both sides close, so cleanup is my responsibility."

"How do you avoid race conditions in IPC?" — "Semaphores for shared memory, message queues when I want the kernel to serialize access naturally. The key is never letting two writers touch the same region without a lock."

"What happens when the process that created a shared memory segment exits?" — "The segment persists. System V IPC objects outlive their creators. You need `shmctl(IPC_RMID)` or `ipcrm` to remove them. I'd always build cleanup into the process exit path or a signal handler."

"When would you use Unix domain sockets?" — "When I need bidirectional communication, file-descriptor passing, or a socket API without TCP overhead. It's my default for local daemon-control protocols."

Clean Up the System, Not Just the Answer

The lifecycle question is where candidates who memorized the API lose points to candidates who have actually run these systems. Pipes clean themselves up when the last file descriptor closes. Shared memory, System V message queues, and semaphores do not. They persist in the kernel until explicitly removed — which means a crashed process can leave IPC objects behind that block or confuse subsequent runs.

`ipcs` lists all active System V IPC objects on the system. `ipcrm` removes them by ID. In a real system, you'd handle cleanup in a signal handler for SIGTERM and SIGINT, or use a wrapper that calls `shmctl(IPC_RMID)`, `msgctl(IPC_RMID)`, and `semctl(IPC_RMID)` at exit. Mentioning `ipcrm` in an interview answer — "and I'd make sure to call `ipcrm` or `shmctl` at exit so the segment doesn't outlive the process" — is the kind of operational detail that makes an interviewer write "production-minded" in their notes.

How Verve AI Can Help You Prepare for Your Systems Engineer Interview

The structural problem this playbook addresses — knowing IPC primitives but freezing on mechanism selection under follow-up — only gets solved through live practice, not more reading. You need someone to ask "why not shared memory here?" and actually respond to what you said, not a canned follow-up that ignores your specific answer.

That's what Verve AI Interview Copilot is built for. It listens in real-time to your answer and responds to what you actually said — the mechanism you named, the tradeoff you mentioned or skipped, the syscall you did or didn't include. If you said "pipe" but didn't mention that it only works for related processes, Verve AI Interview Copilot surfaces that gap immediately. If you named `shmget()` but forgot semaphores, it follows up the way a real interviewer would. The practice sequences that actually build the 30-second answer framework — "what if the interviewer asks about cleanup?" — only work when the tool responds to your live output, not a script. Verve AI Interview Copilot runs those sequences invisibly while you practice, so by the time you're in the real interview, the mechanism-API-tradeoff pattern is a reflex, not a recall exercise.

FAQ

Q: What is interprocess communication in Linux, and why does it matter in real systems and interviews?

IPC is the set of kernel-provided mechanisms that let separate processes exchange data or coordinate access to shared resources, since processes have isolated address spaces by design. In real systems, IPC is how daemons talk to workers, how pipelines move data between stages, and how concurrent processes avoid trampling each other's state. In interviews, it's a proxy for whether you understand the Linux process model and can make engineering decisions under constraint — not just name the mechanisms.

Q: When should you choose shared memory, message passing, pipes, FIFOs, sockets, or semaphores?

Pipes for related-process byte streams. FIFOs when the processes are unrelated and need a named channel. Shared memory when throughput matters and you're willing to own synchronization. Message queues when you need message boundaries and producer-consumer decoupling. Semaphores when the problem is coordination, not data transport — always paired with shared memory. Unix domain sockets when you need bidirectional communication or a socket API without network overhead.

Q: What is the difference between pipes and FIFOs, and when do related versus unrelated processes use them?

A pipe is anonymous — it exists only as file descriptors inherited across `fork()`, so only processes in the same family can use it. A FIFO is a named file in the filesystem that any process can open by path. Use pipes when the communicating processes share a parent; use FIFOs when they don't. The FIFO persists after both sides close, so cleanup is the application's responsibility.

Q: How do message queues, shared memory, and semaphores work at the API and kernel level?

Message queues: `msgget()` creates or opens a queue, `msgsnd()` enqueues a typed message, `msgrcv()` dequeues one, `msgctl(IPC_RMID)` removes the queue. Shared memory: `shmget()` allocates a segment, `shmat()` maps it into the process's address space, `shmdt()` detaches, `shmctl(IPC_RMID)` removes it. Semaphores: `semget()` creates a semaphore set, `semop()` performs atomic wait (`-1`) and signal (`+1`) operations, `semctl()` initializes or removes. All three are System V IPC objects that persist after the creating process exits.

Q: What are the key tradeoffs between speed, safety, complexity, and synchronization overhead?

Shared memory is fastest but requires explicit synchronization and careful cleanup. Pipes and FIFOs are simpler and kernel-managed but pay a copy cost and are one-directional. Message queues add message boundary preservation and decoupling but impose kernel-enforced size limits and management overhead. Unix domain sockets are bidirectional and familiar but slightly more setup than pipes. Semaphores add safety to shared memory but introduce the risk of deadlock if acquired in inconsistent order.

Q: How do race conditions, deadlocks, and starvation show up in IPC interview questions?

Race conditions appear the moment two processes touch shared memory without a semaphore — the interviewer will ask "what happens if both write at the same time?" Deadlock appears when two processes each hold a semaphore the other needs; `SEM_UNDO` in `semop()` mitigates the crashed-process case. Starvation is rarer but appears when a high-priority process continuously preempts a lower-priority one waiting on a semaphore. Naming all three proactively — and explaining which API flags address them — is what makes the answer complete.

Q: What Linux system calls and commands should you mention when answering IPC questions confidently?

For pipes: `pipe()`, `mkfifo()`, `socketpair()`. For shared memory: `shmget()`, `shmat()`, `shmdt()`, `shmctl()`. For message queues: `msgget()`, `msgsnd()`, `msgrcv()`, `msgctl()`. For semaphores: `semget()`, `semop()`, `semctl()`. For inspection and cleanup: `ipcs` to list active IPC objects, `ipcrm` to remove them. Mentioning `ipcs` and `ipcrm` in the context of lifecycle management is a reliable signal that you've worked with these mechanisms in a real environment.

Q: How do you explain IPC in a way that sounds practical rather than purely textbook memorization?

Lead with the constraint, not the mechanism. Say "for this problem, the bottleneck is copy cost, so I'd use shared memory — but that means I need a semaphore for coordination and explicit cleanup at exit." That answer structure — constraint, mechanism, tradeoff, mitigation — sounds like someone who has made the decision before, because it mirrors the actual engineering thought process rather than a definition retrieved from memory.

Conclusion

You don't need to memorize every IPC primitive before your next interview. You need one clean decision framework: start from the constraint, name the mechanism, name the syscall, name the tradeoff, and mention cleanup. That sequence — applied to one prompt, with one set of syscalls — is worth more than a complete survey of every IPC object in the kernel.

Before your next interview, take one prompt — "when would you use shared memory over pipes?" — and practice the 30-second answer using the mechanism-API-tradeoff structure from Section 8. Time yourself. If you can say it cleanly in under thirty seconds and survive a follow-up about semaphores, you're ready for the real thing.

JM

James Miller

Career Coach

Ace your live interviews with AI support!

Get Started For Free

Available on Mac, Windows and iPhone