セマフォを使ってみました
(2005.12.30)
自分のホームページのどれにアクセスがあるのか知りたくなりました。しかしながらプロバイダのカウンタは5ページしか設定でき
ません。そこでホームページカウンタを作って、自宅のサーバで稼働させ、全部のページに設置しました。
テキストファイルの中に見られたページのURLと回数を入れるようにしたので、2人が同時にうちのホームページを見に来ると、
テキストファイルの更新漏れが発生します。それを防ぐものを捜したところ「セマフォ」が有用であることがわかりました。
しかしながら、googleで簡単に検索してみると、具体的な使用例を見つけられなかったので、ここに紹介します。
間違えていたら、エンリョなくご指摘ください。
セマフォを作ってsemvalを1にします。Linuxを起動する度に1回だけ実行します。
----------init.pl----------
#!/usr/bin/perl -w
$IPC_KEY = 1234;
$IPC_CREATE = 0001000;
$id = semget($IPC_KEY, 1, 0666 | $IPC_CREATE);
print "semget(create) id = $id\n";
$id = semget($IPC_KEY, 0, 0);
print "semget(get) id = $id\n";
$semop = pack("sss", 0, 1, 0);
semop($id, $semop);
排他制御ができてるか確認してみます。test1.plはsemvalから1を引いて10秒間待って1を足します。
1個目のtest1.plを起動して「end -1」が表示されたのを見てから、2個目のtest1.plを起動すると「start -1」が表示された所
で停止します。1個目のtest1.plの「end -1」の表示から10秒後に「end 1」が表示されると同時に2個目のtest1.plから「end -1」
が表示されます。これで、test1.plの1番目のsemopから2番目のsemopまでの間は1個の処理だけ(=排他)となりました。
----------test1.pl----------
#!/usr/bin/perl -w
$IPC_KEY = 1234;
$id = semget($IPC_KEY, 0, 0);
print "get id = $id\n";
$| = 1;
print "start -1\n";
$semop = pack("sss", 0, -1, 0);
semop($id, $semop);
print "end -1\n";
sleep(10);
print "start 1\n";
$semop = pack("sss", 0, 1, 0);
semop($id, $semop);
print "end 1\n";
CGIプログラムで利用する場合は、こんな感じになります。
下記の中でのlogupdateはテキストファイルを更新するサブルーチンです。これを2個のsemopで挟むことでlogupdate(テキスト
ファイルの更新処理)が必ず1度に1個だけとなります。
----------accesslog.cgi(の一部)----------
(前処理)
$IPC_KEY = 1234;
$id = semget($IPC_KEY, 0, 0);
$semop = pack("sss", 0, -1, 0);
semop($id, $semop);
logupdate($referer, $logfull, $matchbase);
$semop = pack("sss", 0, 1, 0);
semop($id, $semop);
(logupdateや他のサブルーチンを定義)
おまけ:現在のsemvalを表示するスクリプト
2005.12.30
/proc/sysvipc/semにセマフォの一覧があるのを知ったのでこれはもう要りません。
#!/usr/bin/perl -w
$GETVAL = 12;
$IPC_KEY = 1234;
$id = semget($IPC_KEY, 0, 0);
print "semget id = $id\n";
my $junk = 0;
my $semval = semctl($id, 0, $GETVAL, $junk);
print "semctl semval = $semval\n";
セマフォってこんな感じ。
私が使う分での話ですけれど、semvalの1を取り合っているようなものですね。
「$semop = pack("sss", 0, -1, 0); semop($id, $semop);」で1を手に入れて(他の人=タスクが手に入れていれば、手に入るまで
待ちます)肝心のやりたい事をやってから「$semop = pack("sss", 0, 1, 0); semop($id, $semop);」で1を手放します。
あ、今このページを書いていてinit.plでsemopやってるけどsemctlのSETVALが自然だと気がつきました。