ステートメント有料サービスシェアウェアパートナー(代理店)契約パートナー専用オーダーメイドCGIFAQお問い合わせこのページを印刷ロックファイルを考えるCGIでファイルを操作すると、しばしばデータが消失することが有ります。これは、同時アクセス(書き込み)による衝突です。有名サイトのカウンタさえ破壊されていることがしばしばです。このコーナーでは、衝突しない。万が一衝突してもデータを破壊しないロック機能を考えてみましょう。@DATA;close(OUT);unlink($tmpfile);このコードを1つずつ解析してみましょう。}sleep(1);}これは、UNIXのsymlink関数を使って、1つのファイルに2つめのファイル名を作ろうとしています。symlink関数は成功すると「1」を、失敗すると「0」を返します。この場合失敗する原因は現在、$tmpfileがすでに存在するからです。(他のプロセスが書込み中)成功すればループから抜出し、ファイルに書き込んでいます。@DATA;close(OUT);失敗すると、1秒間に1回、10回までトライします。書き込み後、ダミーファイルを削除してロックを解除しています。unlink($tmpfile);欠点このロック方式には多くの欠点が有ります。symlink関数が衝突することです。symlinkが衝突すると何も返してくれません。これが10回続くと通常のロックなしと、同様に書込んでしまいます。つぎに、たとえロックに成功しても、書き込み時のサーバーダウンにはどうしようもありません。衝突したと同じ事です。それは、データファイルが書き込み可能のパーミッション「666」だからです。これではロックファイルとは呼べません。アルゴリズムとしてもおかしいところが有ります。ロックに失敗した場合もunlink($tmpfile);が実行されると言うことです。テンポラリーファイルを使うこれらの問題を解決するため、いろいろな方法を考えてみましょう。テンポラリーファイルを使う方法です。perlにも実行プロセスを管理するプロセス番号が有ります。これは決して重複することがありません。このプロセス番号のテンポラリーファイルを作成し、データはここに書き出すのです。同じファイルに書き込むのではありませんから決して衝突することがありません。@DATA;cloce(TMP);rename($tmp_dummy,$datafile);最後にファイル名を戻しています。この方法だとデータファイルのパーミッションも「644」読込み専用のままですから、サーバーダウンによる消失も回避できます。これでも問題は有ります。リネームする時に衝突することです。リネームは、データ書き込みの数百分の一の時間で処理できますが、衝突する可能性は0ではありません。ここまでのロック方ならWindowsサーバでも使用できます。テンポラリーファイルのロックファイルリネームの衝突を避けるため、テンポラリーファイルのロックファイルを作成してみましょう。}unlink($tmp_dummy);このコードは、ハードリンクを使用してテンポラリーファイルにロック用として、もう1つのファイル名を付けています。link()関数も、symlink()関数同様に成功すれば「1」を、失敗すれば「0」を返します。成功した場合だけリネームしています。成功しなければリネームしないのですから衝突することはありません。アルゴリズムも問題ありませんね。することでファイルが無いのに削除するようなこともありません。現在でもsymlink()をサポートしないサーバーが多いためです。完成かな?これで一応のロックファイルが完成しました。少々の事では衝突しない。万が一衝突してもデータファイルは破壊されないシステムです。失敗した場合は、クライアントからのリクエストがキャンセルされてしまいます。共有で使用しているサーバーですし、ページが表示される時間が遅くなってしまいますので、永遠に待たせるわけにも行きません。何かいい方法はないものでしょうか?プロセスチェック前節までは、link()でリネームのタイミングを計っていましたが、これでは低速です。早くタイミングを見付け、link()が1回で成功するようにしてみましょう。link()が1回で成功すれば、リネームもすぐに実行できます。}#10秒間トライしても駄目ならリクエストをキャンセル}}このコードを追加することで、どうせ駄目なら無駄なコードは実行しないシステムの出来上りです。link()関数や、symlink()関数をサポートしないNTサーバでは使用出来ません。そこで今度は、Windowsサーバでも使用できる方法を考えてみましょう。考え方はまったく同じです。}}}これでWindowsサーバでもほぼ完璧なロックが出来るでしょう。Windowsサーバのにはすでに存在するファイル名にリネームできない設定になっている物もあります。この場合は、テンポラリを作成するロックシステムは使用できません。多少ロック能力は低下しますが、次の方法でロックできます。数多いサーバの中にはPerlのプロセス番号が取得できない物も有るようです。そこで、多少ロックする能力は低下しますが使用できるロックシステムを考えましょう。これは、link()や、symlink()も使用しませんのでほとんどのサーバで有効です。$tmpfile;last;}}}#ロック中なら1秒待つsleep(1);}$flag;}最後にたった3行で済んでいた書き込みコードがこんなに長くなってしまいましたが、効果抜群です。中途半端なロックなら速度を低下させるだけで、効果も期待できません。大切なデータは、少し位面倒でも強力なロックファイルを使用することをお勧めします。最新版は、当サイトがフリーで公開しております関数ライブラリ「perl-lib.pl」に収録されています。