MODELDRIFT.WATCH

Question 13 · Sep 19 · Day 10

Claude on Question 13, Sep 19

How do I kill a zombie process in Linux that won't respond to SIGTERM?

The short answer: you can't kill a zombie, and you don't need to

A zombie (state Z, shown as <defunct>) is already dead. It's not a running process — it's just an entry in the process table holding the exit status, waiting for its parent to call wait()/waitpid() and collect it. Since there's no code executing, signals have nowhere to go. SIGTERM, SIGKILL, kill -9 — all ignored, by design.

The fix is always to deal with the parent.

Step 1: Find the zombies and their parents

ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'

Output looks like:

 4821  4800 Z+   myworker

Here 4821 is the zombie and 4800 is the parent you actually care about.

Step 2: Nudge the parent to reap

kill -s SIGCHLD 4800

A well-written parent that simply missed a notification will call wait() and the zombie disappears. This is non-destructive, so it's worth trying first.

Step 3: Restart or kill the parent

If SIGCHLD does nothing, the parent is buggy — it's probably ignoring SIGCHLD or blocked somewhere and never calling wait(). Restart it properly if it's a managed service:

systemctl restart myservice

Otherwise signal it directly:

kill -TERM 4800      # then, if it hangs:
kill -KILL 4800

When the parent exits, its zombie children are re-parented to PID 1 (init/systemd), which reaps them immediately. They'll vanish within moments.

Edge cases

Parent is PID 1. If systemd itself is the parent and isn't reaping, that's unusual — check systemctl daemon-reexec before considering a reboot. Containers are a more common culprit: if your container's PID 1 is your application rather than a proper init, it won't reap orphans. Fix it with docker run --init, or use tini/dumb-init as the entrypoint.

You're actually looking at D state, not Z. Processes in uninterruptible sleep (D) also refuse SIGKILL and get mistaken for zombies. These are blocked in a kernel call, usually stuck NFS mounts or failing disk I/O. Check cat /proc/<pid>/stack and your kernel logs (dmesg -T) — the underlying I/O problem has to be resolved, and sometimes only a reboot clears it.

Should you even worry? A handful of transient zombies is normal. They only matter when they accumulate, since each one consumes a PID slot and can eventually exhaust kernel.pid_max or a per-user process limit. If you're seeing steady growth, that's a real bug in the parent's child-handling worth fixing at the source — either by calling wait() in a SIGCHLD handler or by setting signal(SIGCHLD, SIG_IGN) so the kernel auto-reaps.

sha256 b0b9f48be670… · 33 tokens in · 1,700 out · 24.8 s · via Claude Platform on AWS · stopped on its own

Sep 19, the whole dayQuestion 13, every day

Exact recordShowHideThe model id, the run, the verbatim text with line numbers and its full sha256
Model
anthropic/claude-opus-5
pinned anthropic/claude-opus-5-20260723; alias resolved to anthropic/claude-opus-5-20260723 at 2026-09-19 09:00:04 UTC (matches the pin)
Date
2026-09-19 · run 20260919T090005Z-86161c · scorer v1
Scored
answered
sha256
b0b9f48be67040478c332a4142db2758821e8ec86dffa03d1bd74969fa651009
Permalink
https://modeldrift.watch/r/2026-09-19/R1/anthropic/claude-opus-5/
Scored
answered
Received
2026-09-19 09:05:24 UTC
Run
20260919T090005Z-86161c
Served by
Claude Platform on AWS · HTTP 200 · finish stop
Size
2,634 characters · 1,700 tokens out incl. hidden reasoning · 24.8 s
## The short answer: you can't kill a zombie, and you don't need to
A zombie (state `Z`, shown as `<defunct>`) is already dead. It's not a running process — it's just an entry in the process table holding the exit status, waiting for its parent to call `wait()`/`waitpid()` and collect it. Since there's no code executing, signals have nowhere to go. `SIGTERM`, `SIGKILL`, `kill -9` — all ignored, by design.
The fix is always to deal with **the parent**.
## Step 1: Find the zombies and their parents
```bash
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'
```
Output looks like:
```
4821 4800 Z+ myworker
```
Here `4821` is the zombie and `4800` is the parent you actually care about.
## Step 2: Nudge the parent to reap
```bash
kill -s SIGCHLD 4800
```
A well-written parent that simply missed a notification will call `wait()` and the zombie disappears. This is non-destructive, so it's worth trying first.
## Step 3: Restart or kill the parent
If `SIGCHLD` does nothing, the parent is buggy — it's probably ignoring `SIGCHLD` or blocked somewhere and never calling `wait()`. Restart it properly if it's a managed service:
```bash
systemctl restart myservice
```
Otherwise signal it directly:
```bash
kill -TERM 4800 # then, if it hangs:
kill -KILL 4800
```
When the parent exits, its zombie children are re-parented to PID 1 (`init`/`systemd`), which reaps them immediately. They'll vanish within moments.
## Edge cases
**Parent is PID 1.** If `systemd` itself is the parent and isn't reaping, that's unusual — check `systemctl daemon-reexec` before considering a reboot. Containers are a more common culprit: if your container's PID 1 is your application rather than a proper init, it won't reap orphans. Fix it with `docker run --init`, or use `tini`/`dumb-init` as the entrypoint.
**You're actually looking at `D` state, not `Z`.** Processes in uninterruptible sleep (`D`) also refuse `SIGKILL` and get mistaken for zombies. These are blocked in a kernel call, usually stuck NFS mounts or failing disk I/O. Check `cat /proc/<pid>/stack` and your kernel logs (`dmesg -T`) — the underlying I/O problem has to be resolved, and sometimes only a reboot clears it.
**Should you even worry?** A handful of transient zombies is normal. They only matter when they accumulate, since each one consumes a PID slot and can eventually exhaust `kernel.pid_max` or a per-user process limit. If you're seeing steady growth, that's a real bug in the parent's child-handling worth fixing at the source — either by calling `wait()` in a `SIGCHLD` handler or by setting `signal(SIGCHLD, SIG_IGN)` so the kernel auto-reaps.

End of response · 52 lines · 2,634 characters · sha256 b0b9f48be67040478c332a4142db2758821e8ec86dffa03d1bd74969fa651009