しかし、ゾンビプロセス(defunct)とDステートプロセスに kill コマンドを何度打っても解消しません。どちらもカーネルの仕組み上、シグナルを受け取れる状態にないからです。
この記事では、「ゾンビプロセス kill できない」「Dステート kill できない」という状況の根本原因から、ps・/proc ファイルシステムを使った調査手順と状態ごとの安全な解消方法まで、RHEL 9.4 / Rocky Linux 9 の実際のサーバー出力例を交えて解説します。闇雲に kill を繰り返すのではなく、状態を正確に診断して的確な対処が取れるようになることがゴールです。
この記事のポイント
・ゾンビ(defunct/Z)は「終了済みの残骸」でシグナルを受け取れない
・Dステートは「カーネルI/O待ちで割り込み不可」のため kill が効かない
・ゾンビ解消には kill ではなく「親プロセスへの SIGCHLD か親の終了」が正解
・DステートはNFS・ストレージのI/O根本原因を取り除かないと解消しない
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
ゾンビプロセス(defunct)とDステートプロセスはなぜkillできないのか
まず前提として、Linux カーネルにおけるプロセスの「状態(State)」を整理します。ps aux の STAT 列に表示される一文字がプロセスの現在の状態を表しており、kill コマンドで送れるシグナルが効くかどうかは、この状態に完全に依存します。
下表にプロセス状態と kill の有効性をまとめます。
| STAT | 状態名 | kill シグナルの効果 | 代表的な原因 |
|---|---|---|---|
| R | Running(実行中 / 実行待ち) | 有効 | CPU 上で実行中 |
| S | Sleeping(割り込み可能スリープ) | 有効 | I/O 待ちだが割り込み可 |
| D | Disk Wait(割り込み不可スリープ) | 無効(I/O 完了まで保留) | NFS 応答なし、ストレージI/O停止 |
| Z | Zombie(ゾンビ) | 無効(実行コードがない) | 親プロセスが wait() を呼んでいない |
| T | Stopped(停止中) | SIGCONT か SIGKILL のみ有効 | SIGSTOP / Ctrl+Z |
ゾンビとDステートで kill が効かない理由は、それぞれ全く異なるメカニズムによるものです。
1. ゾンビプロセスに kill が効かない理由
ゾンビプロセスとはすでに実行を終えたプロセスの「抜け殻」です。プロセスは終了すると、自分の終了ステータスをカーネルのプロセステーブルに記録して待機します。この終了ステータスを親プロセスが wait() 系システムコールで回収するまでの間、プロセステーブル上のエントリが残り続けます。
この状態にあるプロセスには、実行すべきコードが一切存在しません。CPU もメモリも消費していません。プロセステーブルのスロット 1 つを占有しているだけです。そのため、SIGKILL(シグナル 9)を送っても「受け取るコードが存在しない」ため、何も起こりません。
2. DステートプロセスにSIGKILLが効かない理由
Dステート(uninterruptible sleep)のプロセスは、カーネルの重要なI/O処理の途中にあります。NFS マウントへのファイル読み書きや、ストレージドライバとのやり取りなど、中断すると整合性が崩れる処理の中にいる状態です。
Linux カーネルは、このような「割り込んではいけない」処理の中にいるプロセスをDステートとしてマークし、シグナルの配送を一時的にブロックします。SIGKILL もシグナルの一種であるため、Dステート中は配送が保留され、I/O が完了するか、またはI/Oがエラーで終了するまでずっと待ち続けます。
NFS サーバーが応答を返さない場合のように、I/O がいつまでも終わらない状況では、Dステートのプロセスはシステムを再起動するまで解消しないことがあります。
ゾンビプロセス(defunct)の正体と調査手順
ゾンビプロセスの調査は「どの親プロセスがゾンビを生み出しているか」を特定することが目的です。ゾンビ本体は無害ですが、大量に発生している場合は親プロセスのバグを示しているため、放置すべきではありません。
1. ゾンビプロセスの検出
まず ps コマンドでゾンビプロセスの一覧を確認します。STAT 列に「Z」が付いているプロセスがゾンビです。
# ゾンビプロセスのみを抽出する $ ps -eo pid,ppid,stat,comm | grep ' Z' PID PPID STAT COMMAND 3412 3401 Z php-fpm 3419 3401 Z php-fpm 4892 4880 Z worker.py
上記の例では、PID 3401 の親プロセスの子として php-fpm ゾンビが 2 つ、PID 4880 の親の子として python ゾンビが 1 つ発生しています。
ps aux の表示では COMMAND 列に「[プロセス名] <defunct>」と表示されます。
$ ps aux | awk '$8 ~ /Z/' USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND www-data 3412 0.0 0.0 0 0 ? Z 10:23 0:00 [php-fpm] <defunct> www-data 3419 0.0 0.0 0 0 ? Z 10:24 0:00 [php-fpm] <defunct> appuser 4892 0.0 0.0 0 0 ? Z 11:05 0:00 [worker.py] <defunct>
2. 親プロセス(PPID)の特定
ゾンビプロセスの PPID から親プロセスを特定します。
# ゾンビの親PIDを取り出す $ ps -eo pid,ppid,stat,comm | awk '$3 ~ /Z/ {print $2}' | sort -u 3401 4880 # 親プロセスの詳細を確認する $ ps -p 3401 -o pid,ppid,stat,comm,cmd PID PPID STAT COMMAND CMD 3401 1 S php-fpm php-fpm: master process (/etc/php-fpm.conf)
3. /proc ファイルシステムで詳細を確認する
ゾンビプロセスの /proc エントリには、終了ステータスなどの最低限の情報が残っています。
$ cat /proc/3412/status Name: php-fpm State: Z (zombie) Tgid: 3412 Pid: 3412 PPid: 3401 ... VmPeak: 0 kB VmSize: 0 kB
VmSize が 0 kB であることに注目してください。ゾンビプロセスはメモリを消費していないことが分かります。消費しているリソースはプロセステーブルのエントリ 1 つだけです。
4. ゾンビ数が多い場合のシステムへの影響
Linux のプロセスIDには上限(通常は 32768、/proc/sys/kernel/pid_max で確認可)があります。ゾンビが大量に蓄積するとプロセスIDが枯渇し、新しいプロセスを起動できなくなります。
# 現在のゾンビ数を確認する $ ps -eo stat | grep -c '^Z' 127 # pid_max の上限を確認する $ cat /proc/sys/kernel/pid_max 32768
ゾンビが 100 を超えてきたら、原因となっている親プロセスのバグを優先して調査・修正する必要があります。
Dステート(uninterruptible sleep)プロセスの正体と調査手順
Dステートプロセスは「何かを待っている」状態です。何を待っているかを突き止めることが解消への第一歩です。
1. Dステートプロセスの検出
# Dステートプロセスの一覧を確認する $ ps -eo pid,ppid,stat,comm | grep ' D' PID PPID STAT COMMAND 891 2 D kworker/u4:2 2244 2230 D python3 5512 5500 D rsync
STAT 列が「D」で始まるプロセスがDステートです。「Ds」「D+」のように後ろにフラグが付く場合も同じです。
2. wchan で「何を待っているか」を確認する
/proc/PID/wchan には、プロセスが待機しているカーネル関数名が記録されています。これが Dステート調査の核心情報です。
# 待機しているカーネル関数を確認する(NFS の場合の例) $ cat /proc/2244/wchan nfs_file_read # rsync がブロックしている場合の例 $ cat /proc/5512/wchan nfs_lock_inode_wait # ディスクI/O待ちの場合の例 $ cat /proc/891/wchan io_schedule
wchan の値から原因を推測できます。
・nfs_*:NFSマウントへのI/O待ち → NFSサーバーの応答不能を疑う
・io_schedule:ブロックデバイスのI/O待ち → ストレージの異常を疑う
・mutex_lock* / wait_event*:カーネル内ロック待ち → ドライバのバグの可能性
3. カーネルスタックで詳細を確認する
root 権限があれば /proc/PID/stack でカーネルスタックの詳細を確認できます。これにより待機の連鎖を追うことができます。
# カーネルスタックを確認する(root権限が必要) $ sudo cat /proc/2244/stack [<0>] nfs_file_read+0x72/0xe0 [nfs] [<0>] new_sync_read+0x12d/0x1c0 [<0>] vfs_read+0xa4/0x1a0 [<0>] ksys_read+0x5f/0xe0 [<0>] do_syscall_64+0x3b/0x90 [<0>] entry_SYSCALL_64_after_hwframe+0x72/0xdc
スタックの最上位(先頭行)が現在ブロックしている場所です。この例では NFS のファイル読み込み関数で止まっていることが分かります。
4. dmesg でカーネルメッセージを確認する
DステートはカーネルがI/Oエラーを記録していることが多く、dmesg に手がかりがあります。
$ dmesg | tail -30 [1234567.890] nfs: server 192.168.10.50 not responding, timed out [1234580.123] nfs: server 192.168.10.50 not responding, still trying [1234621.456] INFO: task python3:2244 blocked for more than 120 seconds. [1234621.457] Not tainted 5.14.0-427.13.1.el9_4.x86_64 #1 [1234621.458] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
「blocked for more than 120 seconds」はカーネルの hung task 検出メッセージです。この PID を突き合わせると Dステートプロセスの原因が即座に特定できます。
5. lsof でオープンファイルを確認する
Dステートプロセスがどのファイルやソケットを開いたままブロックされているかは、lsof コマンドで確認できます。
# Dステートプロセスのオープンファイルを確認する $ sudo lsof -p 2244 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME python3 2244 appuser cwd DIR 0,46 4096 123456 /mnt/nfs_share python3 2244 appuser 0u CHR 136,0 0t0 3 /dev/pts/0 python3 2244 appuser 1u CHR 136,0 0t0 3 /dev/pts/0 python3 2244 appuser 4r REG 0,46 1073741824 789012 /mnt/nfs_share/data/large_file.bin
cwd(カレントディレクトリ)や開いているファイルが NFS マウントポイント(この例では /mnt/nfs_share)を指している場合、NFS の問題が原因です。
ゾンビプロセスを安全に消す方法:親プロセスへのアプローチ
ゾンビプロセスを消すには、親プロセスに「子プロセスを回収してください」と伝えるか、親プロセスを終了させてカーネル(init/systemd)にゾンビを引き取らせるかの、2 つのアプローチがあります。
1. 親プロセスに SIGCHLD を送って子を回収させる
適切に実装されたプログラムは SIGCHLD シグナルを受け取ると wait() を呼び出してゾンビを回収します。まずこの穏やかな方法から試します。
# 親PIDに SIGCHLD を送る(PPID=3401 の場合) $ kill -CHLD 3401 # または数値シグナルで指定する $ kill -17 3401 # 送信後にゾンビが消えたか確認する $ ps -eo pid,ppid,stat,comm | grep ' Z' (何も表示されなければ回収成功)
ただし、バグのあるプログラムや SIGCHLD を無視する設定のプロセスでは効果がない場合があります。
2. 親プロセスを kill して systemd にゾンビを引き取らせる
SIGCHLD が効かない場合、または親プロセスを停止して問題ないと判断した場合は、親プロセスを終了させます。親プロセスが終了すると、そのゾンビ子は PID 1(systemd / init)に引き取られ、systemd が自動的に wait() を呼んで回収します。
# 親プロセスを穏やかに終了させる(SIGTERM) $ kill 3401 # 数秒待ってゾンビが消えたか確認する $ ps -eo pid,ppid,stat,comm | grep ' Z' # SIGTERM で終了しない場合は SIGKILL(サービスの場合は systemctl で管理する方が安全) $ kill -9 3401
注意:親プロセスが Web アプリや DB のマスタープロセスである場合、kill するとサービスが停止します。サービス管理下にあるプロセスは systemctl restart サービス名 で再起動するほうが安全です。
# php-fpm サービスを再起動する(これでゾンビも一緒に解消される) $ sudo systemctl restart php-fpm # 再起動後の確認 $ ps -eo pid,ppid,stat,comm | grep ' Z' (ゾンビが消えていることを確認)
3. 親プロセスが PID 1(systemd)の場合
稀に親プロセスが PID 1 の systemd そのものである場合があります。この場合、systemd が何らかの理由で wait() を遅延させているケースが多く、通常はしばらく待つと自動回収されます。systemd 自体を kill することは絶対に行わないでください。
# PPIDが1のゾンビを確認する $ ps -eo pid,ppid,stat,comm | awk '$3 ~ /Z/ && $2 == 1 {print}' # systemd に孤児プロセス回収を促す(SIGCHLD を PID 1 に送る) $ sudo kill -CHLD 1
Dステートプロセスの解消手順と判断フロー
Dステートの解消は、I/O ブロックの原因を取り除くことが唯一の正解です。原因が解消されると、待機中のシステムコールがエラーを返し、プロセスが D から抜けます(正常終了か、プログラム内のエラーハンドリングが動く)。
1. NFS マウントが原因の場合
wchan に nfs_ が含まれる場合は NFS が原因です。NFS サーバーとの通信を確認します。
# NFS マウント一覧を確認する $ mount | grep nfs 192.168.10.50:/export/data on /mnt/nfs_share type nfs (rw,relatime,...) # NFS サーバーへの疎通を確認する $ ping -c 3 192.168.10.50 # または $ showmount -e 192.168.10.50 # 強制アンマウント(-l は lazy unmount、-f は force) $ sudo umount -l -f /mnt/nfs_share
強制アンマウント後、Dステートプロセスが D から抜けてプロセスが終了または再実行されることを確認します。
# アンマウント後のプロセス状態確認 $ ps -eo pid,ppid,stat,comm | grep ' D' (Dステートが解消されていれば何も表示されない)
NFS マウントを soft オプション(-o soft,timeo=30,retrans=3)で設定しておくと、タイムアウト後にI/Oがエラーを返すため Dステートになりにくくなります。
2. ブロックデバイス(HDD/SSD)のI/Oエラーが原因の場合
wchan が io_schedule または blk_mq_* 系の場合は、ストレージデバイスのI/Oエラーを疑います。
# ストレージのI/Oエラーを確認する $ dmesg | grep -E 'I/O error|ata[0-9]+|blk_update_request' [1234567.890] blk_update_request: I/O error, dev sdb, sector 1048576 [1234568.012] ata2.00: exception Emask 0x0 SAct 0xf00 SErr 0x0 action 0x6 [1234568.014] ata2.00: irq_stat 0x40000008 # iostat でデバイスのI/O状況を確認する $ iostat -x 1 3
物理ディスクのI/Oエラーが原因の場合、当該デバイスの交換が必要になります。RAID 構成であれば縮退稼働したうえでデグレード状態を確認し、ディスク交換と再ビルドを行います。
3. それでも解消しない場合の最終手段
NFS 強制アンマウントやデバイス確認でも Dステートが解消しない場合、カーネルパニックを起こさずにシステムを再起動するのが最も安全な手段です。
# 通常の再起動(シャットダウンプロセスを経由) $ sudo systemctl reboot # シャットダウンプロセスが Dステートで詰まった場合は magic SysRq を使う # (/proc/sys/kernel/sysrq が 1 の場合のみ有効) $ echo 1 | sudo tee /proc/sys/kernel/sysrq $ echo b | sudo tee /proc/sysrq-trigger # ※ echo b は即時リブート(ファイルシステムの同期なし)。最終手段として使う
SysRq による即時リブートはファイルシステムの同期を行わないため、ファイル破損のリスクがあります。まず echo s | sudo tee /proc/sysrq-trigger(sync)を実行してからリブートするとリスクを軽減できます。
両者を素早く見分けるチェックリスト
障害対応中に状況を素早く把握するためのチェックリストです。
# ゾンビとDステートを一発で確認するコマンド $ ps -eo pid,ppid,stat,comm | awk '$3 ~ /^[ZD]/' PID PPID STAT COMMAND 891 2 D kworker/u4:2 2244 2230 D python3 3412 3401 Z php-fpm
| 確認項目 | ゾンビ(Z) | Dステート(D) |
|---|---|---|
| STAT 列 | Z(または Zs, Z+) | D(または Ds, D+) |
| COMMAND 列 | [プロセス名] <defunct> | 実際のコマンド名 |
| CPU / メモリ消費 | 0%(リソースを消費しない) | 低いが長時間継続すると高くなる |
| kill -9 の効果 | 無効 | 無効(I/O完了後に効く場合あり) |
| 調査コマンド | ps -eo pid,ppid,stat でPPID確認 |
cat /proc/PID/wchan で待機関数確認 |
| 解消方法 | 親プロセスへ SIGCHLD / 親プロセスを終了 | I/O根本原因の解消(NFS再マウント等) |
| 緊急度 | 大量でなければ低い(CPU/メモリ影響なし) | 高い(プロセスが進まずサービスに影響) |
再発防止策とカーネルメッセージの読み方
1. ゾンビ再発防止:アプリケーション側の修正
ゾンビが継続的に発生する場合、親プロセスのコードに wait() が実装されていないか、シグナルハンドラで SIGCHLD を無視(SIG_IGN)に設定しているバグがあることがほとんどです。
・C/C++ アプリ:waitpid(-1, NULL, WNOHANG) を SIGCHLD ハンドラ内でループ呼び出しする
・Python アプリ:signal.signal(signal.SIGCHLD, signal.SIG_DFL) でデフォルトに戻すか、subprocess モジュールの .wait() を適切に呼ぶ
・シェルスクリプト:バックグラウンドプロセスには wait コマンドを対応させる
2. Dステート再発防止:NFS マウントオプションの見直し
NFS マウントの際に hard(デフォルト)ではなく soft オプションを使うと、タイムアウト後に I/O がエラーを返すため Dステートになりにくくなります。
# /etc/fstab での NFS soft マウントの設定例 # 192.168.10.50:/export/data /mnt/nfs_share nfs soft,timeo=30,retrans=3,_netdev 0 0 # コマンドラインでのマウント例 $ sudo mount -o soft,timeo=30,retrans=3 192.168.10.50:/export/data /mnt/nfs_share
ただし soft マウントはデータ整合性のリスクがあるため、書き込みを伴うマウントには慎重に適用します。読み取り専用(ro)の NFS マウントであれば soft を積極的に活用できます。
3. カーネルの hung task タイムアウト設定
カーネルは 120 秒以上 Dステートが続くプロセスを「hung task」として dmesg に記録します。この値は調整可能です。
# 現在の hung task タイムアウト値を確認する $ cat /proc/sys/kernel/hung_task_timeout_secs 120 # 検知を早める場合(本番環境では慎重に) $ echo 60 | sudo tee /proc/sys/kernel/hung_task_timeout_secs # 永続化する場合は /etc/sysctl.d/99-hung-task.conf に記述する # kernel.hung_task_timeout_secs = 60
4. 定期監視スクリプトの例
Dステートやゾンビの発生を早期に検知するための簡単な監視スクリプトの例です。
#!/bin/bash # プロセス異常状態の監視スクリプト(cron で 5 分ごとに実行) ZOMBIE_COUNT=$(ps -eo stat | grep -c '^Z') DSTATE_COUNT=$(ps -eo stat | grep -c '^D') if [ "$ZOMBIE_COUNT" -gt 10 ]; then logger -t proc-monitor "WARNING: Zombie count is ${ZOMBIE_COUNT}" fi if [ "$DSTATE_COUNT" -gt 5 ]; then logger -t proc-monitor "CRITICAL: D-state count is ${DSTATE_COUNT}" # カーネルメッセージを記録する dmesg | tail -20 | logger -t proc-monitor fi
このスクリプトを cron に登録しておき、/var/log/messages に記録された警告を Zabbix や Prometheus で拾う構成にすることで、障害の早期発見につながります。
プロセスのトラブルシューティングをさらに深く学ぶには、Linux 基本コマンドの解説も合わせてご覧ください。
まとめ
ゾンビプロセスとDステートプロセスは、どちらも「kill -9 が効かない」という現象が同じでも、原因も対処法も全く異なります。状態を正しく見極めることが障害対応の出発点です。
| やりたいこと | コマンド / 手順 |
|---|---|
| ゾンビ一覧を確認する | ps -eo pid,ppid,stat,comm | grep ' Z' |
| Dステート一覧を確認する | ps -eo pid,ppid,stat,comm | grep ' D' |
| ゾンビの親PIDを取り出す | ps -eo pid,ppid,stat,comm | awk '$3 ~ /Z/ {print $2}' | sort -u |
| Dステートの待機関数を確認する | cat /proc/PID/wchan |
| Dステートのカーネルスタックを確認する | sudo cat /proc/PID/stack |
| 親プロセスにゾンビ回収を促す | kill -CHLD PPID |
| NFSマウントを強制解除する | sudo umount -l -f /mnt/マウントポイント |
| カーネルのI/Oエラーを確認する | dmesg | grep -E 'I/O error|not responding|hung' |
| hung task タイムアウトを確認する | cat /proc/sys/kernel/hung_task_timeout_secs |
Linux Master Pro Seminar の詳細を見る >>
ゾンビとDステートの仕組みを理解することで、kill を繰り返す無駄な時間を削減し、障害の根本原因に素早くたどり着けるようになります。Linux の障害対応スキルをもっと体系的に身につけたい方は、ぜひ下のセミナー情報もご覧ください。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます