ファイヤープロジェクト
forkとwaitとゾンビプロセス
2003-07-20T15:13+09:00   matsu
forkはプロセスのコピーを作成する.そしてwaitで待つ.処理の流れが一本から複数に分かれる様がフォークみたいだからforkなのだろうか.
forkはそれを実行したプロセスの子プロセスを作成する.子プロセスは親プロセスのコピーである.プロセスをどこまで実行したか(プログラムカウンタ?)もコピーされるので,子プロセスはforkの返り値が返るところから実行される(と,思う).あと,ファイルディスクリプタなどもオープンされていればそのままコピーされる.コピーされないのはpidやppidとファイルロックやサスペンド中のシグナルぐらいだろうか.そして,そのforkの返り値によって,プロセスは自分が親プロセスか子プロセスかを知ることができる.
  • 成功すれば子プロセスには0が返り,親プロセスには子プロセスのPIDが返される.
  • エラーの時は親プロセスに-1が返り,子プロセスは生成されない.
-1が返ったときには,errnoに値がセットされているはず.
EAGAIN
親プロセスのページ・テーブルのコピーと子プロセスのタスク構造に生成に必要なメモリをfork が割り当てることができなかった.
ENOMEM
メモリが足りないために,forkは必要なカーネル構造体を割り当てることができなかった. 子プロセスはPIDが固有で,PPIDを親プロセスのPIDに設定されている以外は親プロセスのものがコピーされる.
子プロセスを生成して子プロセスの親プロセスがそれぞれ自分と相手のPIDを表示するサンプルを作成してみた.
実行すると,以下のようになる.
fork done
fork done
child process.  pid = 1261.     my ppid = 1260
parent process. pid = 1260.     my child's pid = 1261
fork doneというのは親プロセスと子プロセスの両方が実行している.また,pidの表示の部分はswitchで分岐しているので子プロセスと親プロセスは別のcaseを実行している.
forkしたら同期を取りたくなるのが人情である.同期にはやはり(?)waitを用いる.waitを呼び出すとそのプロセスは子プロセスのどれかが死ぬか停止するまで待つ.返り値はその子プロセスのPIDである.そしてその子プロセスの終了ステータスはwaitの引数に(NULLでなければ)格納される.終了ステータスはsys/wait.hのマクロで調べることができる.あと,PIDを指定した子を待つのにはwaitpidを使用する.終了ステータスを調べるマクロやwaitpidで指定するPIDについてはmanに詳しく書いてある.
先程のforkのサンプルを修正してこんな風にしてみた.
これを実行すると以下のようになる.
$> ./a.out
fork done
fork done
parent process. pid = 1407.     my child's pid = 1408
$> child process.  pid = 1408.     my ppid = 1
なんか次のプロンプトがでて来てから子プロセスの出力がでる.で,これをwaitを使用して何とかしてみる.
これを実行するとこうなる.
fork done
fork done
parent process. pid = 2036.     my child's pid = 2037
child process.  pid = 2037.     my ppid = 2036
child process done.exit status = 0
目標達成.
先のwaitでは,子が親より先に死んで親からはwaitでその終了ステータスなどを取得できた.これは,子が死んでも,親からのwaitに備えてプロセステーブル内の子のエントリを開放せずに残っていることで実現される.このときの子プロセスがゾンビである.そして子のエントリは親が終了すると開放される.が,親が異常終了すると,時動的にinitが子の親になるらしい.initが終了するのはシステムをシャットダウンするときなので,それまでずっとリソースを消費する(ファイルディスクリプタやプロセステーブルの領域など)ゾンビがいるのは嫌である.という分けで,子より先に死ぬ親は無責任なのかもしれない.そこで,ちょっと子の最後を見届ける親のサンプルを書いてみた.
waitpidのオプションでWNOHANGを指定すると,親プロセスは子プロセスが終了していればそのPIDを,終了していなければ即座に0を返す(子プロセスの終了をまたない).これを実行すると,こうなった.
child process.  pid = 3856.     my ppid = 3855
child process.  pid = 3857.     my ppid = 3855
child process.  pid = 3858.     my ppid = 3855
child process.  pid = 3859.     my ppid = 3855
child process.  pid = 3860.     my ppid = 3855
child process.  pid = 3861.     my ppid = 3855
child process.  pid = 3862.     my ppid = 3855
child process.  pid = 3863.     my ppid = 3855
child process.  pid = 3864.     my ppid = 3855
child process.  pid = 3865.     my ppid = 3855
parent process
PID 3856 done
No child exited
No child exited
PID 3857 done
No child exited
No child exited
PID 3858 done
No child exited
No child exited
PID 3859 done
No child exited
No child exited
PID 3860 done
No child exited
No child exited
PID 3861 done
No child exited
No child exited
PID 3862 done
No child exited
No child exited
PID 3863 done
No child exited
No child exited
PID 3864 done
No child exited
No child exited
PID 3865 done
親は一秒ごとに子の死を確認して全員の死を確認すると自分も死ぬ.出産時のエラーチェックをしていないのが微妙.
matsu(C)
Since 2002
Mail to matsu