セマフォを使ってみました
投稿:2005-12-30
自分のホームページのどれにアクセスがあるのか知りたくなりました。
しかしながらプロバイダのカウンタは5ページしか設定できません。
そこでホームページカウンタを作って、自宅のサーバで稼働させ、全部のページに設置しました。
テキストファイルの中に見られたページのURLと回数を入れるようにしたので、2人が同時にうちのホームページを見に来ると、テキストファイルの更新漏れが発生します。
それを防ぐものを捜したところ「セマフォ」が有用であることがわかりました。
しかしながら、googleで簡単に検索してみると、具体的な使用例を見つけられなかったので、ここに紹介します。
間違えていたら、エンリョなくご指摘ください。
セマフォを作ってsemvalを1にします。Linuxを起動する度に1回だけ実行します。
init.pl
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
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(の一部)
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が自然だと気がつきました。