AlgoMaster Logo

Zombies and Orphans

16 min readUpdated August 7, 2026
Listen to this chapter
Unlock Audio

A child process can finish executing without disappearing immediately. Another child can continue executing even after the process that created it has disappeared.

These two situations produce two memorable Unix terms:

  • A zombie has terminated, but its parent has not yet collected its termination information.
  • An orphan is still alive when its parent terminates, so the operating system assigns it a new parent.

The names sound similar, but the underlying situations are almost opposites. A zombie is dead but still has a process-table record. An orphan is alive but has lost its original parent.

Understanding the distinction matters in real systems. A backend service that launches helpers without collecting them can slowly fill its process table with zombies. A container whose main process does not act as a proper parent can inherit descendants and fail to reap them.

Two-Stage Process Termination

From the child's point of view, termination happens when it calls _exit, returns from main, or is terminated by the operating system. Execution stops and the kernel releases most of the process's resources.

From the parent's point of view, one piece of work remains: learning how the child terminated.

This creates a two-stage lifecycle:

Nothing the child can do moves it out of the zombie state, because the only transition out depends on the parent.

The call that collects the child's status is commonly described as reaping the child.

This design prevents a race. A fast child might finish before its parent reaches waitpid(). Instead of discarding the result, the kernel retains it. When the parent eventually waits, the status is still available and the call can return immediately.

What Exactly Is a Zombie?

A zombie is a child process that has terminated but has not yet been reaped by its parent.

The zombie does not execute instructions. It cannot resume its old work, and it is not waiting for CPU time. The kernel has already released resources such as its user-space memory and open file descriptors.

What remains is a small amount of process bookkeeping needed by the parent, including:

  • The child's process ID
  • Its termination status
  • Resource-usage information
  • Enough parent-child relationship data to support a wait operation

This is why describing a zombie as a “process that is still running” is incorrect. Its computation is over; its termination record is still present.

On Linux, process inspection tools report a zombie with state Z and often display <defunct> in the command column.

The PPID is especially important: it identifies the parent responsible for collecting that zombie.

Why Zombies Exist

Suppose a parent starts a child and then performs other work:

Parent and child run independently after the fork. The child can finish long before the parent asks, and the kernel holds the result in the meantime.

The exit code 42 must remain available until the parent asks for it. The kernel therefore preserves a compact termination record.

That record also tells the parent whether the child:

  • Exited normally
  • Was terminated abnormally
  • Produced a particular exit status

The wait-status macros interpret the encoded status returned by wait() or waitpid():

Once a successful wait consumes the termination record, the child is reaped and its process-table entry can be removed.

Creating and Observing a Zombie

The following program deliberately delays waitpid() so that the child remains a zombie long enough to inspect.

Compile and run it on a Unix-like system:

While the parent is sleeping, use the printed child PID in another terminal:

The child should appear with state Z. After the parent calls waitpid(), running the same command produces no entry because that PID is no longer present.

The twenty-second delay is useful for demonstration but is not a valid reaping strategy. Real programs arrange to collect children promptly.

Why kill Cannot Fix a Zombie

Sending a termination request to a zombie cannot make it “more terminated.” The process has already stopped executing, so there is no running program to receive or act on the request.

The missing action must come from the parent:

Signalling the zombie child is the wrong target, because it has already terminated. The action that resolves it belongs to the parent, which must call wait or waitpid.

If the defective parent terminates, the zombie is reparented to a process that is expected to reap adopted children. That often makes the zombie disappear, but terminating a production parent is a recovery action, not a fix for faulty child management.

The durable fix is to correct the parent so every child it creates is eventually collected.

Why Zombie Accumulation Is Dangerous

One zombie consumes little memory compared with a running process. A zombie no longer owns its former heap, stack, or open sockets.

The problem is the retained kernel bookkeeping. Every unreaped zombie occupies a process-table slot and keeps its PID in use. If a faulty server continuously launches short-lived children, the number of zombies can grow without bound.

Eventually, the machine, user account, service, or container can reach a process-count limit. New calls to fork() or similar creation operations may then fail. The visible symptom might be “cannot create process,” even though the true cause is an old reaping bug.

This pattern appears in backend systems that launch:

  • Command-line utilities for each request
  • Media converters or report generators
  • Short-lived worker processes
  • External scripts with timeout logic

“Fire and forget” is not a complete process-lifecycle policy. If a service creates a child, it must either collect that child or deliberately transfer responsibility to a supervisor that will.

Reaping One Known Child

When a parent cannot proceed until a particular child finishes, blocking waitpid() is straightforward:

Passing a positive PID selects that specific child. With options set to 0, the call blocks if the child is still running and returns immediately if the child has already become waitable.

Each successful call collects one child's status. Waiting once is therefore insufficient when a parent has created several children.

Reaping Without Blocking

A server often has other work to do and cannot pause until an arbitrary child exits. WNOHANG lets waitpid() check for completed children without waiting for a living one.

The PID argument -1 means “select any child.” The loop is essential because several children may terminate before the parent gets an opportunity to run. A single waitpid() call would reap only one of them.

A production event loop can call this drain function whenever it needs to process completed children. It should also associate each PID with the operation that launched it, record success or failure, and remove the child from its own tracking structures.

There are configurations in which the operating system discards child termination records automatically. Those configurations also change whether later wait calls can retrieve status. Explicit reaping is usually the clearest design when exit results matter.

Parent-Child Reaping Restrictions

Ordinary wait() and waitpid() calls operate on the caller's child processes. An unrelated process cannot repair a zombie by waiting for it.

This ownership rule is valuable. Exit status goes to the component that created and managed the work, rather than being consumed unpredictably by another process.

It also makes diagnosis direct:

  1. Locate processes in state Z.
  2. Read each zombie's PPID.
  3. Inspect that parent and the code path that launches children.
  4. Verify that every success, error, and timeout path eventually performs a wait.

If many zombies share the same parent PID, that parent is the first place to investigate.

What Is an Orphan?

An orphan is a process whose parent terminates while the child is still running.

The operating system does not terminate the child merely because its original parent is gone. Instead, it changes the child's parent relationship so that another process becomes responsible for it.

Before parent exitsAfter parent exits
Child's parentThe original parentAn adopting process
Child's PPIDThe original PIDThe adopter's PID
Child's executionStill runningStill running, undisturbed

The child keeps its own PID, address space, open descriptors, and execution state. Its parent PID changes.

Traditionally, orphaned processes are described as being adopted by init, the system's PID 1 process. That is a useful starting model, but modern Linux has two important refinements:

  • A process can declare itself a child subreaper and adopt orphaned descendants before they reach the system's init process.
  • PID namespaces have their own init-like process, so the relevant adopter may be PID 1 as seen inside a container rather than the host's PID 1.

The accurate rule is that Linux reparents an orphan to an appropriate living subreaper or init process for its process hierarchy.

Creating and Observing an Orphan

This program lets the original parent exit while its child remains alive:

Compile and run it:

The pipe ensures that the parent stays alive until the child has printed its original parent PID. After the delay, the original parent is gone and getppid() reports the adopter's PID.

The exact number depends on the environment. On a traditional host it may be 1; under a shell, service manager, test harness, or container runtime, a subreaper may adopt the child instead.

Independent Zombie and Orphan States

The clearest comparison is based on two questions: Is the child still running, and is the original parent still alive?

ConditionChild executing?Original parent alive?What must happen next?
Normal running childYesYesParent continues managing it
ZombieNoUsually yesParent must collect its status
OrphanYesNoKernel reparents it to an adopter
Reaped childNoIrrelevantNo process entry remains

An orphan does not automatically become a zombie. It normally continues running under its new parent.

When that orphan eventually terminates, it can briefly enter the zombie state like any other child. Its adopting parent is then responsible for reaping it.

A zombie can also become orphaned in a bookkeeping sense if its parent terminates without reaping it. The kernel reparents the termination record so that an adopter can collect it.

Loading simulation...

Subreapers and Process Supervisors

Linux allows a process to mark itself as a child subreaper. If a descendant later becomes orphaned, the nearest living ancestor with this role adopts it.

This is useful for service supervisors and process-management frameworks:

Without the subreaper setting, the helper would be adopted somewhere else entirely, becoming the responsibility of a process that knows nothing about it.

Without the subreaper role, the helper could be reparented farther up the process hierarchy. With it, the supervisor can observe and collect descendants whose immediate parents disappear.

Subreaping does not eliminate the need for wait. It changes which process receives the responsibility.

Why PID 1 Matters in Containers

A container often has its own PID namespace. The first process in that namespace appears as PID 1 and serves as its init process.

If an application runs directly as container PID 1, it may adopt orphaned descendants. It must collect them when they terminate. An application that assumes “some system init process will handle this” can therefore leak zombies inside the container.

This commonly happens when:

  1. A request handler launches a tool.
  2. That tool launches another process.
  3. The intermediate process exits.
  4. The remaining descendant is adopted by container PID 1.
  5. PID 1 never performs the required waits.

Container deployments often use a small init or a process supervisor to forward lifecycle events and reap adopted children. Whether that extra component is necessary depends on the process tree and whether the main application correctly manages every descendant it can inherit.

The essential invariant is simple:

Every terminated child must eventually have a living parent or adopter collect its status.

Diagnosing Zombie Leaks

On Linux, this command provides a useful system-wide view:

Look for Z in the STATE column. To filter the output:

For each result, inspect the parent:

Elapsed time helps distinguish a transient zombie from a persistent leak. A zombie can exist briefly in a healthy program between child termination and the parent's next reaping opportunity. A steadily growing count or a zombie that remains for a long time points to broken lifecycle handling.

Useful questions during diagnosis include:

  • Does the parent call waitpid() on every creation path?
  • Does an error return skip cleanup?
  • Does timeout logic terminate a child but forget to reap it?
  • Can several children exit while the code collects only one?
  • Is the process acting as PID 1 or as a subreaper?

Restarting the parent may clear its existing zombies through reparenting, but monitoring after the restart reveals whether the underlying leak remains.

Summary

  • A zombie is a terminated child whose exit information has not yet been collected.
  • Reaping with wait() or waitpid() removes the zombie's remaining process-table record.
  • Zombies do not execute or retain their former address spaces, but large numbers can exhaust process slots.
  • An orphan is a living child whose original parent has terminated.
  • Linux reparents orphans to an appropriate subreaper or init process, which becomes responsible for reaping them later.
  • Reliable services track every child, drain all completed children, and handle adopted descendants when acting as a supervisor or container PID 1.

Quiz

Zombies and Orphans Quiz

5 quizzes