あなたの天然記念物
ホーム更新雑談Perl鉄ゲタランドナーコースガイド自転車Linuxリンク経歴連絡先
セマフォを使ってみました (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が自然だと気がつきました。