MODELDRIFT.WATCH

Question 13 · Oct 1 · Day 22

Claude on Question 13, Oct 1

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

Why signals don't work

A zombie (state Z, sometimes shown as defunct) is already dead. The process has exited and released all its resources — memory, file descriptors, everything. What remains is a tiny entry in the kernel's process table holding its exit status, waiting for the parent to collect it with wait()/waitpid().

Since there's no running code left, there's nothing to deliver a signal to. SIGTERM, SIGKILL, kill -9 — all are no-ops on a zombie. The real problem is the parent process, which isn't reaping its children.

Find the parent

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

Or more readably:

ps -el | grep -w Z
ps -o pid,ppid,stat,cmd --ppid <PPID>   # see all children of that parent

Note the PPID column — that's your actual target.

Fix it

1. Nudge the parent to reap (least disruptive)

SIGCHLD tells the parent a child has died. A well-written parent with a handler will call wait():

kill -s SIGCHLD <PPID>

This often does nothing, because the parent's bug is usually that it ignores or mishandles SIGCHLD in the first place — but it's free to try.

2. Restart the parent

If it's a service, do it properly:

systemctl restart <service>

3. Kill the parent

When the parent dies, its zombie children are reparented to init/systemd (PID 1), which reaps them immediately:

kill <PPID>          # try SIGTERM first
kill -9 <PPID>       # if it won't budge

Be careful here — if the parent is a critical daemon or a session leader, you may take down more than you intend. Check what it actually is before firing.

4. Reboot

Only genuinely necessary if PID 1 itself is failing to reap, or you've exhausted the process table and can't fork new processes.

Is it even worth fixing?

A handful of zombies is harmless. They consume no CPU and no memory beyond a process table slot. The two reasons to care:

  • •PID exhaustion — check your limit with cat /proc/sys/kernel/pid_max (commonly 32768 or 4194304). Thousands of accumulating zombies can eventually prevent new processes from spawning, which looks like a system-wide hang.
  • •It's a symptom — steadily growing zombie count means a leaking parent process. Fix the application rather than playing whack-a-mole.

Watch the trend:

watch -n 5 'ps -eo stat | grep -c "^Z"'

If you're writing the parent

Reap explicitly:

while (waitpid(-1, NULL, WNOHANG) > 0);

Or let the kernel do it — setting SIGCHLD to SIG_IGN makes children be reaped automatically:

signal(SIGCHLD, SIG_IGN);

In containers, this is a common gotcha: your app runs as PID 1 but isn't designed to reap orphans. Use docker run --init, or an init shim like tini or dumb-init as your entrypoint.

sha256 b23537c287a2… · 33 tokens in · 1,347 out · 19.3 s · via Claude Platform on AWS · stopped on its own

Oct 1, 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-10-01 09:00:06 UTC (matches the pin)
Date
2026-10-01 · run 20261001T090007Z-857bc9 · scorer v1
Scored
answered
sha256
b23537c287a218bbd41644e16803a6f6bd0de8edb8957edeb833d6eea75e4be2
Permalink
https://modeldrift.watch/r/2026-10-01/R1/anthropic/claude-opus-5/
Scored
answered
Received
2026-10-01 09:05:06 UTC
Run
20261001T090007Z-857bc9
Served by
Claude Platform on AWS · HTTP 200 · finish stop
Size
2,814 characters · 1,347 tokens out incl. hidden reasoning · 19.3 s
## Why signals don't work
A zombie (state `Z`, sometimes shown as `defunct`) is **already dead**. The process has exited and released all its resources — memory, file descriptors, everything. What remains is a tiny entry in the kernel's process table holding its exit status, waiting for the parent to collect it with `wait()`/`waitpid()`.
Since there's no running code left, there's nothing to deliver a signal to. `SIGTERM`, `SIGKILL`, `kill -9` — all are no-ops on a zombie. The real problem is the **parent process**, which isn't reaping its children.
## Find the parent
```bash
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'
```
Or more readably:
```bash
ps -el | grep -w Z
ps -o pid,ppid,stat,cmd --ppid <PPID> # see all children of that parent
```
Note the `PPID` column — that's your actual target.
## Fix it
**1. Nudge the parent to reap (least disruptive)**
`SIGCHLD` tells the parent a child has died. A well-written parent with a handler will call `wait()`:
```bash
kill -s SIGCHLD <PPID>
```
This often does nothing, because the parent's bug is usually that it ignores or mishandles `SIGCHLD` in the first place — but it's free to try.
**2. Restart the parent**
If it's a service, do it properly:
```bash
systemctl restart <service>
```
**3. Kill the parent**
When the parent dies, its zombie children are reparented to `init`/`systemd` (PID 1), which reaps them immediately:
```bash
kill <PPID> # try SIGTERM first
kill -9 <PPID> # if it won't budge
```
Be careful here — if the parent is a critical daemon or a session leader, you may take down more than you intend. Check what it actually is before firing.
**4. Reboot**
Only genuinely necessary if PID 1 itself is failing to reap, or you've exhausted the process table and can't fork new processes.
## Is it even worth fixing?
A handful of zombies is harmless. They consume no CPU and no memory beyond a process table slot. The two reasons to care:
- **PID exhaustion** — check your limit with `cat /proc/sys/kernel/pid_max` (commonly 32768 or 4194304). Thousands of accumulating zombies can eventually prevent new processes from spawning, which looks like a system-wide hang.
- **It's a symptom** — steadily growing zombie count means a leaking parent process. Fix the application rather than playing whack-a-mole.
Watch the trend:
```bash
watch -n 5 'ps -eo stat | grep -c "^Z"'
```
## If you're writing the parent
Reap explicitly:
```c
while (waitpid(-1, NULL, WNOHANG) > 0);
```
Or let the kernel do it — setting `SIGCHLD` to `SIG_IGN` makes children be reaped automatically:
```c
signal(SIGCHLD, SIG_IGN);
```
In containers, this is a common gotcha: your app runs as PID 1 but isn't designed to reap orphans. Use `docker run --init`, or an init shim like `tini` or `dumb-init` as your entrypoint.

End of response · 84 lines · 2,814 characters · sha256 b23537c287a218bbd41644e16803a6f6bd0de8edb8957edeb833d6eea75e4be2