devutils/ATFQ/book ATFQ git

Part II · Your system

16.1 Process states and zombies

alsozombiezombiesdefunctprocess stateprocess statesps statuninterruptible

TL;DR

  • Every process has a one-letter state: R running, S sleeping, D waiting on I/O, T stopped, Z zombie.
  • Most processes are S, waiting for something. That's normal.
  • A zombie has already exited. Only its entry is left, until its parent reads how it ended.
  • Zombies use no CPU and almost no memory, but each one holds a PID.
  • You can't kill a zombie: fix or restart its parent.
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'

On your machine

alice@laptopexample
$ atfq 16.1
  Total     8 processes: 1 running, 3 sleeping, 1 waiting on I/O (D), 1 idle
            kernel thread, 2 zombies
  Zombies   PID 4242 worker (old), parent 4100 supervisor
            PID 4300 orphan-job, parent 9999
  Waiting   PID 5000 rsync, parent 1 systemd

An example: Alice's laptop, one of the test machines. In your terminal, this block shows your own machine, read live.

Pitfalls

Deep dive 3 questions

Why do zombies exist at all?

When a process exits, the kernel frees its memory and closes its files, but keeps a small record: its PID, its exit code and the resources it used. The parent collects that record with the wait() family of system calls. Until it does, the dead child shows up as a zombie (<defunct> in ps).

Without this record, a parent couldn't learn whether its child succeeded, failed, or was killed by a signal. Once the parent reads it, the entry disappears: the child has been "reaped".

What happens to children when their parent dies?

They become orphans and are adopted by PID 1 (usually systemd), or by the closest ancestor that registered itself as a "subreaper" (possible since kernel 3.4). PID 1 collects its adopted children, so their zombies vanish. That's why stopping the parent of a zombie gets rid of it.

Containers are the classic trap: if your app runs as PID 1 and never calls wait(), zombies pile up. A tiny init such as tini (what docker run --init adds) fixes that.

What's the difference between S and D?

Both are sleeping. In S (interruptible sleep), a signal wakes the process up: Ctrl-C works. In D (uninterruptible sleep), the kernel holds signals back, because stopping halfway through would leave something inconsistent, usually a disk read or write inside a driver.

D normally lasts milliseconds. When it lasts minutes, the cause is below the process: an NFS server that stopped answering, a failing disk, or a driver bug. Since kernel 2.6.25 some waits are "killable": kill -9 gets through, even though ps still shows D.

Commands
ps top
Paths
/proc/PID/stat /proc/PID/status
See also
16.2 Signals
18.1 Load average