MODELDRIFT.WATCH

Question 13 · Sep 26 · Day 17

Claude on Question 13, Sep 26

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

Why SIGTERM does nothing

A zombie process isn't running — it's already dead. The Z (or Z+/defunct) state means the process has exited, but its parent hasn't called wait()/waitpid() to collect the exit status. The kernel keeps a minimal process table entry alive just to hold that status. There's no code executing, so there's nothing to deliver a signal to. kill -9 won't help either.

The fix is always to get the parent to reap it.

Find the zombies and their parents

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

Or to see the whole tree context:

ps -ef --forest | grep -i defunct

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

Step 1: Nudge the parent

kill -s SIGCHLD <ppid>

This works if the parent has a working SIGCHLD handler that simply missed a notification. It's harmless to try and occasionally fixes things instantly.

Step 2: Restart or kill the parent

If the parent is buggy (common with poorly written daemons and some container init setups), terminate it:

kill -TERM <ppid>
# if it hangs:
kill -KILL <ppid>

When the parent dies, the zombie is re-parented to PID 1 (init/systemd), which reaps orphans automatically. The zombie disappears immediately.

If the parent is a managed service, prefer:

systemctl restart <service>

Step 3: Accept them, or reboot

If the parent is PID 1 itself, or a process you can't restart, you're stuck — there is no way to remove a zombie directly, even as root. Reboot is the only option.

That said, a handful of zombies is harmless. Each consumes one PID slot and a few hundred bytes of kernel memory. Only worry if the count is climbing toward your PID limit:

cat /proc/sys/kernel/pid_max
ps -eo stat | grep -c Z

A likely mis-diagnosis

If a process is genuinely ignoring SIGKILL, it's probably not a zombie but in uninterruptible sleep — state D:

ps -eo pid,stat,wchan:30,comm | awk '$2 ~ /^D/'

D means it's blocked in a kernel syscall, usually on I/O: a hung NFS mount, a failing disk, a stuck FUSE filesystem, or a misbehaving driver. Signals are queued but not delivered until the syscall returns. Check dmesg for I/O errors and cat /proc/<pid>/stack (as root) to see where it's wedged. Fixing the underlying I/O problem — remounting the NFS share, clearing the device error — is the only way out short of a reboot.

Preventing it in your own code

Either reap children explicitly, or tell the kernel you don't care:

signal(SIGCHLD, SIG_IGN);  /* kernel auto-reaps */

In containers, run a proper init as PID 1 (docker run --init, or tini) so orphaned children get reaped.

sha256 d36e8efe39c8… · 33 tokens in · 1,797 out · 26.0 s · via Claude Platform on AWS · stopped on its own

Sep 26, 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-26 09:00:06 UTC (matches the pin)
Date
2026-09-26 · run 20260926T090007Z-89c0b7 · scorer v1
Scored
answered
sha256
d36e8efe39c8f8e607540ba8512dd2acff42ed503e3dcf673e4396dbfe470a40
Permalink
https://modeldrift.watch/r/2026-09-26/R1/anthropic/claude-opus-5/
Scored
answered
Received
2026-09-26 09:05:09 UTC
Run
20260926T090007Z-89c0b7
Served by
Claude Platform on AWS · HTTP 200 · finish stop
Size
2,703 characters · 1,797 tokens out incl. hidden reasoning · 26.0 s
## Why SIGTERM does nothing
A zombie process isn't running — it's already dead. The `Z` (or `Z+`/`defunct`) state means the process has exited, but its parent hasn't called `wait()`/`waitpid()` to collect the exit status. The kernel keeps a minimal process table entry alive just to hold that status. There's no code executing, so there's nothing to deliver a signal to. `kill -9` won't help either.
The fix is always to get the **parent** to reap it.
## Find the zombies and their parents
```bash
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'
```
Or to see the whole tree context:
```bash
ps -ef --forest | grep -i defunct
```
Note the PPID column — that's your actual target.
## Step 1: Nudge the parent
```bash
kill -s SIGCHLD <ppid>
```
This works if the parent has a working `SIGCHLD` handler that simply missed a notification. It's harmless to try and occasionally fixes things instantly.
## Step 2: Restart or kill the parent
If the parent is buggy (common with poorly written daemons and some container init setups), terminate it:
```bash
kill -TERM <ppid>
# if it hangs:
kill -KILL <ppid>
```
When the parent dies, the zombie is re-parented to PID 1 (`init`/`systemd`), which reaps orphans automatically. The zombie disappears immediately.
If the parent is a managed service, prefer:
```bash
systemctl restart <service>
```
## Step 3: Accept them, or reboot
If the parent is PID 1 itself, or a process you can't restart, you're stuck — there is no way to remove a zombie directly, even as root. Reboot is the only option.
That said, **a handful of zombies is harmless.** Each consumes one PID slot and a few hundred bytes of kernel memory. Only worry if the count is climbing toward your PID limit:
```bash
cat /proc/sys/kernel/pid_max
ps -eo stat | grep -c Z
```
## A likely mis-diagnosis
If a process is genuinely ignoring `SIGKILL`, it's probably **not** a zombie but in uninterruptible sleep — state `D`:
```bash
ps -eo pid,stat,wchan:30,comm | awk '$2 ~ /^D/'
```
`D` means it's blocked in a kernel syscall, usually on I/O: a hung NFS mount, a failing disk, a stuck FUSE filesystem, or a misbehaving driver. Signals are queued but not delivered until the syscall returns. Check `dmesg` for I/O errors and `cat /proc/<pid>/stack` (as root) to see where it's wedged. Fixing the underlying I/O problem — remounting the NFS share, clearing the device error — is the only way out short of a reboot.
## Preventing it in your own code
Either reap children explicitly, or tell the kernel you don't care:
```c
signal(SIGCHLD, SIG_IGN); /* kernel auto-reaps */
```
In containers, run a proper init as PID 1 (`docker run --init`, or `tini`) so orphaned children get reaped.

End of response · 76 lines · 2,703 characters · sha256 d36e8efe39c8f8e607540ba8512dd2acff42ed503e3dcf673e4396dbfe470a40