ゾンビプロセス(zombie process)の正体と対処

ゾンビプロセス(zombie process)の正体と対処

ゾンビプロセスとは何か?

終了したのにプロセステーブルから消えないプロセスがゾンビプロセス(状態コード Z)。プロセス自体の実行はすでに終わっており、CPU・メモリは消費しない。ただし PID とプロセステーブルのエントリだけが残り続ける。

$ ps aux | grep Z
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
www-data  1234  0.0  0.0      0     0 ?        Z    10:01   0:00 [defunct]

STAT 列が Z または [defunct] と表示されるのがゾンビの特徴。

なぜゾンビプロセスが発生するのか?

親プロセスが子の終了ステータスを回収していないことが原因。

Linux では子プロセスが終了すると、カーネルは終了ステータスをプロセステーブルに保存して親に SIGCHLD シグナルを送る。親が wait() / waitpid() でステータスを回収するまで、子のエントリはテーブルに残り続ける。

  • 親が wait() を呼ばずに子を終了させた
  • 親が SIGCHLD ハンドラを適切に実装していない
  • 親自体にバグがあり、シグナルを処理できない状態になっている

ゾンビは親プロセスが消えない限り自分では消えないkill -9 をゾンビに送っても効果なし(すでに実行中ではないため)。

ゾンビプロセスの詳細な確認方法

親プロセスを特定する

$ ps -el | grep Z
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 Z  1000  1234  1100  0  80   0 -     0 -      pts/0    00:00:00 process

PPID(親プロセス ID)が 1100 とわかる。次に親プロセスを確認する。

$ ps aux | awk 'NR==1 || $2==1100'

top でゾンビ数を監視する

$ top
Tasks: 142 total,   1 running, 141 sleeping,   0 stopped,   2 zombie

zombie の数が継続的に増加している場合は問題あり。

ゾンビプロセスの対処法

方法 1: 親プロセスに SIGCHLD を送る

親プロセスが SIGCHLD を処理できる実装なら、シグナルを送ると wait() を呼び出してゾンビを回収する。

$ kill -SIGCHLD <PPID>

方法 2: 親プロセスを終了させる

親が終了すると、子(ゾンビ)の養親が init(PID 1)または systemd に変わり、自動的に回収される。

$ kill <PPID>

親が応答しない場合:

$ kill -9 <PPID>

サービスプロセス(nginx、mysqld 等)を親ごと kill する場合は、サービスへの影響を事前に確認すること。

方法 3: システム再起動

ゾンビが大量発生して正常な状態に戻せない場合の最終手段。システム再起動でプロセステーブルは初期化される。

ゾンビプロセスは危険か?

少数のゾンビは実害なし。CPU・メモリを消費しないため、1〜2 個程度であれば即座に対処する必要はない。

ただし次の場合は問題になる:

  • PID 枯渇: Linux のデフォルト PID 上限(/proc/sys/kernel/pid_max)は 32768。ゾンビが大量に蓄積すると PID が尽き、新しいプロセスを起動できなくなる
  • プロセステーブルの肥大化: カーネルリソースを消費し続ける

現在の PID 上限確認:

$ cat /proc/sys/kernel/pid_max
32768

ゾンビを生まないアプリケーション設計

ゾンビはアプリケーションのバグが表出したもの。根本対処はコード修正。

  • SIGCHLD ハンドラで waitpid(-1, NULL, WNOHANG) をループ呼び出し
  • double fork パターンで孫プロセスを init に引き取らせる
  • SIGCHLDSIG_IGN に設定する(POSIX 準拠の実装では子の自動回収が保証される)

次に読む