fork 子プロセスに制限時間を設ける。 | 世界的日曜WEBプログラマー日記

世界的日曜WEBプログラマー日記

一年後に世界的なWEBサービスを運営するにはどうすればいいのか書いていく(予定)

os.fork 子プロセスに制限時間をもうけ、一定時間以上は親プロセスがKillする方法


宅配弁当サイトを運用している、等々力です。

タイムアウトを検知するプログラムの説明。
あるプロセスがネットワーク系のプロセスでハマると、抜け出せなくなっってプログラムが終わらない場合がある。
その場合、forkを使って分岐した子プロセスに制限時間を持たせて処理させることができる。

alarmで子供プロセスを待つ時間を設定できる。
まった後はsignal.signalでセットしたコールバック関数を呼びに行く。

なお、alarmで待つ時間を調節ができる。
プログラム上、alarmを呼ぶ前にsignal.signalをよぶ必要があるので、注意。signalの第二引数にはコールバック関数を自前の仕掛けておく。
意外と大変だった。

お弁当宅配のサイトを作っているけど、バックグラウンドではここまでしているんですねー。

import signal, os

# コールバック関数。singal.signalで指定。
def timeout_checker():
raise RuntimeError

while True:
child_id = os.fork()
if child_id != 0: # 親プロセス
signal.signal(signal.SIGALRM, timeout_checker ) # コレは必須
signal.alarm(10); # 10秒間待ってみる。
try:
os.waitpid( child_id, 0 )
NORMAL_END = True
except RuntimeError:
os.kill( child_id, signal.SIGKILL ) # 子プロセスを強制終了

NORMAL_END = False
signal.alarm(0)
if NORMAL_END
print '子プロセスは正常終了'
continue
else:
print '子プロセスは異常終了'
else: # 子プロセス
child_process_doit( )
os._exit(0) # will be die.
以上ですが、まぁめんどかった。


Fork解説



os.fork()を呼ぶ前までは、ただ一つのプロセスしかメモリーにはない。
os.fork()を呼んだ後には二つのプロセスがメモリーにはある状態になり、
1. 親プロセス、
2. 子プロセス
とそれぞれ呼ぶ。
子プロセスは親プロセスが完全にコピーされる。
完全にコピーとは、プログラマー目線からみて、同じ変数、同じ値、os.fork()がコールされる全く同じスタートポイントという意味。

世界的日曜WEBプログラマー日記-maishoku| fork


プロセス管理はos.fork()のリターンバリューにより行う。
親プロセスには子プロセスのプロセスIDが入り、
子プロセスには0が入る。これでどちらのプロセスかが管理できる。

親プロセスがどうやって子供プロセスが死んだかどうかを検知する方法には全部で二つある。
1つがos.waitpid(),それからSignal.

UNIXの世界では子プロセスが死んだ時にSIGCHLDが呼んで管理するSignalな方法があるが、おすすめできない。
例えば子供プロセスがForkしまくって8つ位になると、メンテナンス性にさがるため。
そのためSignalを使うより、os.waitpidでの管理がスマートである。。

尚戻り値は、次のようになる。
(pid,status) = os.waitpid(0,os.WNOHANG)
一つはプロセスID、もうひとつがステータスコード。
なお、なにも終っていなかったら(0,0)が帰ってくる。


今回のコードの流れの解説。
alarmで指定時間経過後、コールバック関数time_outを呼びに行く。
time_outの中で例えば、raise RuntimeErrorを定義した。
os.waitpidの時に、終わっていなければ、本例外が発呼されることになる。
終わっていなければ、呼ばれることはない。


株式会社OctOpt
コンピューターサイエンス会社OctOptの技術公式ブログ
等々力 康弘
@rocky_house