ファイルのロックに関する基礎知識ロックはなぜ必要か?たとえばカウンターを作成する際に、以下のような処理を行うとします。"$count\n";ところが、この実装には問題があります。Aさんが5行目を実行し終えて6行目を実行するまでの間の僅かのタイミングに、Bさんが1行目と2行目を行うと、Bさんは空のファイルを読んでしまうことになります。1を加算すると値は1。これをBさんがファイルに書き込むと、カウンターファイルの破壊(カウンター値のリセット)が発生してしまいます。10000回くらいループするスクリプトを作成し、2つ同時に起動してみましょう。ファイルが壊れる様子を目で確認することができると思います。これを防ぐためには、「今私が書き込んでいるから、他の人は読んだり書いたりしないでね」という処理を行う必要があります。これを排他制御とかファイルをロックすると呼びます。flock()によるロック制御Perlにはflock()というロック制御専用の関数が用意されています。flock()の使用法や問題点は、「とほほのperl入門」のflock()を参照してください。flock()を用いたカウンターの例は次のようになります。95/98や一部のUNIXなど、flock()を使用できないOSがあるため、flock()の代わりにシンボリックリンクやディレクトリを用いたロック機構を使用することがあります。シンボリックリンクはUNIXのファイルの一種で、Windowsのショートカットファイル、Macのエイリアスファイルのようなものです。最初にこのロック方法を実現した人がシンボリックリンクを用いたためか、シンボリックリンクの例が広く紹介されていますが、ここでは、Windowsにも流用できることを考えて、ディレクトリを用いた例を紹介します。ロックを実現する上で両者に機能の差異はありません。ロックディレクトリを用いた、まずい例ロックを実装する際に、まずやってしまいそうなのが、次のような誤ったロック方法です。ロックディレクトリを消すロックディレクトリが無ければ、誰も処理中ではないということなので、自分がロックディレクトリを作成して「処理中だよ」と宣言して、処理に入ります。ロックディレクトリがすでに存在していれば、誰かが処理中ということなので、あきらめて退散します。一見するとうまく動きそうですが、1行目でロックディレクトリの有無を調べてから2行目でロックディレクトリを作成するまでの間に隙間があるため、この隙間にBさんが同じ処理をやっていると、2人とも「他に誰もロックディレクトリを作っていないので、自分が処理をはじめちゃお」という状態になってしまいます。上記の例では、「ロックディレクトリが無い」=「誰も処理中でない」=「自分が処理してもよい」と判断しているところに問題があります。これを次のように改造します。ロックディレクトリを消す「ロックディレクトリが無ければ」+「作成して」の部分を「ロックディレクトリをまず作ってみて作成できたら」に変更しています。mkdir()は、すでにロックディレクトリが存在する場合は失敗し、まだロックディレクトリが存在していなければ作成して成功を返します。複数の人がほぼ同時にこの処理をやろうとしても、mkdir()処理はOS内部(カーネル)が行うため、決して重複することはありません。ロック権を取れなかったら再トライする次のようにして、ロック権をとれなかった時にあきらめず、何回か再トライすることができます。}ロックディレクトリが残ってしまうのを防ぐしかしまだ問題があります。誰かがロックディレクトリを作成したまま、その人が何かの原因で異常終了してしまっていた場合、永遠にロックディレクトリが残ったままとなり、以後、誰もロック権を得ることができなくなってしまいます。異常終了する際にも、ロックディレクトリを削除するようにしておきましょう。上記のループを抜けた直後に次の行を挿入します。サブルーチンを呼び出し、ロックディレクトリの削除(掃除)をしています。これでも完璧ではありません。mkdir()をやった後、シグナルの宣言を行うまでの間に異常終了するとロックディレクトリは残ってしまうし、先にシグナルの処理を宣言してしまうと、mkdir()が失敗した時にも、誤って自分が人のロックディレクトリを消してしまうことになります。OS自体が突然止まった場合や、SIGKILLなどハンドリングできないシグナルで異常終了した場合などは、やはりロックディレクトリは残ってしまいます。mkdir()に失敗した場合は、ロックディレクトリの日付を見て、一定期間以上古い場合は、ロックディレクトリが何らかの原因で残ってしまったのだと判断して、消してしまうのも手です。(私はこの方法を採用しています。)追記:古いと判断したファイルを削除すると、古いと判断して削除するまでの隙間に、他のプログラムも古いと判断して削除して新しく作成している可能性があるというご指摘をいただきました。使わないロック方法の限界なのでしょうか・・・(2000.8.27追記)でも、flock()によるロックであれば、スクリプトが異常終了しても、OSが異常終了しても、ロックが残ってしまうことは絶対にないため、flock()が使えるならその方が安全です。ロックしておけばファイルは壊れないかロックを行ったとしても、ファイルが壊れない訳ではありません。ロックとは別の理由でファイルは壊れます。close(OUT);なんてことをやっていると、1行目と2行目の間でロックもしていないのにファイルが空になっている状態が発生し、この時に別の人がファイルを読んで処理して書き込むと・・・ファイルは壊れます。他にも、ファイルにデータを書き込んでいる最中に、ディスクが溢れた、システムが異常停止したなどの理由で、ファイルは壊れます。結論ロックしておかないとファイルは壊れます。ロックをしていてもファイルは壊れます。下手なロックをしているとよく壊れます。下手をするとロックしない時よりもよく壊れます。上手なロックをしていると壊れる確率を減らすことができます。ロックはその前後で別ファイルで行う・・・なんてことをすると、少しは壊れにくくなるかもしれません。初版:2000年6月4日、最終更新:2003年8月24日http://www.tohoho-web.com/wwwcgi8.htm。