世界的日曜WEBプログラマー日記 -17ページ目

世界的日曜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

seleniumで特定のキーワード例えば「弁当 配達 マイ食 」を取得するスクリプト、を定期的に実行方法


SeleniumとかFirefoxを使うのでCronTabにそのまま書いただけでは定期的にコマンドを実行できない。するためには以下のようにする。


crontabの起動
$ crontab -e

次を追加

# 朝七時に起動する。
0 7 * * * export DISPLAY=:0 && /usr/bin/bento_maishoku.py

以下より抜粋なり。
Ubuntu Tutorial: How to launch an GUI from cron in Ubuntu

これでお弁当のランキングスクリプトをSeleniumで取得している。
お腹すいタナー。

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

PythonでPingを使う場合、
$ subprocess.Popen( [ 'ping' ] ...
といったようなダサいやり方をやらなければならないのが嫌。
探してみると、pingのライブラリがあった。インストールはpipで行う。
( pipはPython用のパッケージ管理システム)

$ pip install ping
でインストール。できたーーー!と安心できない。
これはICMPというローエンドを叩いているのでルート権限でないとうまく使えないのだ。
(python non-privileged ICMPとかそんなエラーが出る。)
意味ねー。

しょうがないのでsetcapでcap_net_rawをpythonに与えてやることに。
$ sudo setcap cap_net_raw=ep /usr/bin/python2.7
これで一般ユーザーでも使えることになった。

一つ注意としてsetcapでは絶対パスを指定すること(シンボリックリンクじゃダメ。)
パスの調べ方は
$ which python
やls -lahtを組み合わせて見つける。

setcapとは、プロセスに細かく権限を与えてやるコマンド。Pythonコマンドに今回はraw部分を触る権限を与えていることになる。
実はコレ結構危険かもしれない。。まぁ自分だけしか触らないなら大丈夫だけどね。

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