cswork / libraryOpen practice workspace

Articles

Shared Memory Interview Questions: IPC Lifetimes, Synchronization, and Crash Recovery

Practice shared memory interview questions on POSIX IPC lifetimes, initialization, process-shared locks, owner death and partial-record recovery.

Shared memory interview questions test whether you can make shared bytes into a correct protocol. Mapping one region into two processes solves access to the same storage. It does not establish initialization order, mutual exclusion, record ownership or recovery after a writer dies.

Start with Explain Low-Latency Linux Networking and Shared-Memory Trade-Offs. Practice a small two-process handoff before discussing low-latency optimization.

Evidence boundary: This guide concerns POSIX shared memory on Linux, using current Linux man-pages and the linked POSIX mutex specification. It does not describe CUDA shared memory, System V IPC or a distributed-memory service. The generation ledger and crash-recovery exercise are original designs, not candidate reports or employer requirements. Linux-specific behavior and application assumptions are identified where they matter.

Shared memory interview questions: what has a lifetime?

The Linux POSIX shared-memory overview separates creating or opening an object, sizing it, mapping it, unmapping it and removing its name. Explain those operations individually rather than saying “free the shared memory.”

A process has a file descriptor referring to an object and a mapping in its address space. The object also has a namespace entry while its name exists. These relationships do not end together.

The shm_open reference states that closing the descriptor after mapping does not invalidate the mapping. shm_unlink removes the name; existing mappings can continue using the old object. Recreating that name creates a distinct object.

Use this original ledger for two processes that already mapped /queue:

ActionProcess AProcess BNamespace consequence
A closes its descriptorIts mapping remains usableIts mapping remains usableName still refers to original object
A unmaps its regionA loses that mappingB retains its mappingName can still exist
Owner unlinks /queueExisting mappings are not redirectedExisting mappings remain on the original objectNew opens without creation fail
Another process recreates /queueOld mapping still refers to old objectOld mapping still refers to old objectName now refers to a new object

The final row explains a subtle restart bug: a new worker joins generation 2 while a surviving worker still reads generation 1. The string name matches, but they no longer communicate through the same region. An application generation identifier and controlled join procedure help detect that mismatch.

Who creates and initializes the shared structure?

A newly created POSIX object has zero length. Use the documented sizing operation, ftruncate, before relying on a mapped layout. Check failures from creation, sizing and mapping separately; a valid descriptor does not prove the region has the expected size.

For the original queue design, designate one initializer. It creates the object with an exclusive-creation check, sizes it, initializes the header and synchronization objects, then publishes readiness through a separately defined control mechanism. Joiners validate size, layout version and generation before using the payload.

Exclusive creation elects who created the name; it does not mean initialization is complete when a second process sees it. A ready flag also needs a synchronization protocol. Reading a plain integer while another process writes it is not a sufficient publication contract.

Specify what happens if the initializer dies before readiness. A supervisor can retire that generation and start a new one, provided it first controls admission and surviving users. Do not let every joiner independently truncate or reinitialize the same object. Reinitializing a mutex that other processes may still use is not a recovery strategy.

Keep the shared layout explicit: fixed-width fields where appropriate, lengths, offsets, version and capacity. Validate a joiner's interpretation of that layout rather than trusting a cast to a structure from a different build.

Why does MAP_SHARED not make access synchronized?

The mmap reference distinguishes shared mappings from private copy-on-write mappings. Shared visibility is necessary for this IPC design, but a multi-field update still needs synchronization and an ownership rule.

Suppose a producer writes a payload, length and queue position. A consumer must not observe a new queue position with an incomplete payload. Two producers must not claim the same slot. Define the critical section or publication protocol that prevents those states.

For a first interview design, keep the queue's reads and writes under a process-shared mutex. The process-shared mutex attribute reference distinguishes PTHREAD_PROCESS_SHARED from the default process-private attribute. Place the initialized mutex in storage that participating processes actually share, and check whether the implementation supports the requested attribute.

volatile does not provide mutual exclusion or a transaction over several fields. Likewise, replacing a mutex with an atomic flag requires a complete memory-ordering, waiting and recovery design. Do not assume every language atomic implementation is suitable across process boundaries; establish the platform and representation requirements.

The semaphore overview describes a separate synchronization mechanism. A count can represent available items or free slots, but a counting protocol still needs a crash policy. A producer that reserves capacity and dies can leave the logical count inconsistent with committed records.

Can another process dereference a pointer stored in the region?

Not merely because the pointer's bytes are shared. The processes may map the object at different virtual addresses. A pointer into A's heap is also unrelated to B's heap. Prefer a validated offset from the mapping base for the original layout.

For example, a record might store offset 256 and length 64. Each process computes its own base plus 256 after validating that the range lies inside the mapped size. Check arithmetic without overflow: verify the offset is at most the region size and the length is at most the remaining size.

Objects containing ordinary heap pointers, allocator state or process-private synchronization cannot become a portable shared layout by copying them into the region. A std::vector of records may refer to an allocation outside it. Explain how storage ownership, construction and destruction work, or use a deliberately simple record format.

Changing capacity while peers retain the old mapping introduces another lifecycle boundary. Define an admission stop and remap procedure, or create a new generation. The mmap documentation's beyond-object access warnings make unchecked sizing particularly dangerous; do not treat the original address range as proof that the underlying object is still large enough.

What does EOWNERDEAD tell you?

The Linux robust mutex documentation describes PTHREAD_MUTEX_ROBUST. If its owner dies while holding the lock, a later successful acquisition can return EOWNERDEAD, signaling that protected state may be inconsistent.

Crucially, the caller owns the mutex in that case. Treating every nonzero return as “lock was not acquired” can skip required repair and leave the protocol broken. The linked POSIX mutex-lock specification states that distinction.

For the original queue, use this pseudocode outline:

result = lock(shared_robust_mutex)
if result == EOWNERDEAD:
    inspect records and reconstruct the queue invariant
    if repair cannot establish consistency:
        unlock without declaring consistency
        stop admitting work to this generation
    else:
        declare_mutex_consistent()
        perform the guarded operation
        unlock()
elif result == 0:
    perform the guarded operation
    unlock()
else:
    handle the error without assuming ownership

The pthread_mutex_consistent reference assigns repair responsibility to the application. Calling it does not roll back a half-written record or restore a missing acknowledgment. It declares that you have already restored the required invariant.

The robust-mutex reference also describes ENOTRECOVERABLE after unlocking inconsistent state without making it consistent. That requires controlled retirement or reconstruction; blind retrying does not make the old state trustworthy. Check return values from the consistency and unlock operations in an implementation too.

How do you recover a partially written record?

For this original exercise, all slot metadata and payload access occurs under the initialized process-shared robust mutex. Each record has a sequence number, length and state. A producer marks a slot PREPARING, fills it, then marks it COMMITTED. Consumers act only on committed records.

The marker names are an application protocol, not POSIX features. They help recovery only if every writer follows the declared ordering and the recovery routine can validate the structure. A checksum may provide additional corruption evidence; it neither locks the record nor proves that an external side effect happened.

Crash windowWhat recovery may observeDeclared recovery action
Before slot reservationNo new recordLeave committed queue unchanged
During payload constructionPREPARING recordDiscard incomplete slot and reconstruct occupancy
After commit, before notificationCOMMITTED record, missing wakeupPreserve the record and restore the wakeup/count protocol
After external work, before acknowledgmentCommitted record without completion evidenceReconcile or replay using an external idempotency contract

Specify how recovery finds authoritative records. A queue counter might itself have been interrupted. Reconstruct it from validated slot states and sequence relationships when that is the design's source of truth; do not automatically trust whichever field is convenient.

A committed record must have a valid range and expected sequence. If those checks fail, quarantine or retire the generation according to the exercise's policy. Declaring consistency while uncertain about the payload changes a detectable failure into silent corruption.

Does crash recovery imply durability or exactly once?

No. The shared-memory overview describes kernel persistence and its shutdown boundary. An object surviving one process's death is not durable storage across a machine reboot. If the requirement includes restart after power loss, introduce a separately specified durable log or database and recovery protocol.

The external-work row creates a different ambiguity. A consumer may successfully submit a payment, file write or downstream task and die before recording completion. Another consumer cannot determine the outcome solely from shared bytes. An operation ID, downstream deduplication or outcome reconciliation can resolve that boundary when the external system supports it.

This is a design inference: exactly-once claims require the complete effect-and-acknowledgment contract, not just a robust mutex. Explain which component owns authoritative outcome state and what happens when it is unavailable. A queue's locally consistent state can still contain work with an unknown external result.

How would you test shutdown and restart?

Exercise process death at the initialization, preparing, committed and acknowledgment boundaries. Restart a participant and validate generation, layout and admission rules before allowing new work. Include a surviving participant during unlink and recreation to expose split generations.

Test full and empty queues, simultaneous producers, malformed lengths and wrong layout versions. Verify that blocked participants can observe shutdown and exit according to the chosen signaling mechanism. Cleanup ownership should be explicit: a supervisor or designated owner retires the name after coordinating users.

Measure performance after correctness is established. Contention, false sharing, memory placement and busy waiting can change latency even when no payload copies are needed. “Zero copy” is a description of a particular data path, not a blanket guarantee of low overhead or safe ownership.

Sources and Further Reading

Research checked October 4, 2026. The pseudocode is a design exercise; Linux process-shared robust-mutex execution was not run for this article.