ファイルアクセスの排他処理


CGIで重要視されるファイルロック機構について解説します。
訪問者数が非常に多いサイトでは、ある人のアクセスによりCGIが ファイルを読み書きしている間に、他の人もそれを読み書きしてしまう ことが起こります。訪問カウンタを例に、Aさん、Bさん2人が同時に近い タイミングでカウンタCGIを実行した場合に、 カウンタ記録ファイルのデータがどう変化していくか見てみましょう。

Aさんが実行したCGI カウント記録 Bさんが実行したCGI
open 1000
read 1000を読む 1000 open
count 1001にする 1000 read 1000を読む
write 1001を書く 1001 count 1001にする
close 1001 write 1001を書く
1001 close
これで明らかなように、BさんのCGIは間違った値をファイルに上書き してしまいます。もしwriteとreadが同時に起こったら酷いことになるかも しれません。

このような現象を防ぐ方法は、BさんのCGIのファイルopenをAさんの CGIのファイルcloseまで待たせることです。このファイルロックの芸当は OSにちょっと依頼するだけで簡単に実現できます。 もちろんUNIXでの話です。ただしUNIXでもBSD系のOS(FreeBSD, NetBSD, OpenBSDなど) と他のUNIX(Linux,SunOS,HP-UX,DEC-OSなど)ではロックに用いる関数が 異なり、BSD系では flock関数、非BSD系では lockf関数を使います。 どのUNIXでも同じコードで済ませたいなら以下のようにして 非BSD系でもflock関数を使えるようにします。

#include <fcntl.h>
#ifndef LOCK_EX
#define LOCK_EX F_LOCK
#define LOCK_UN F_ULOCK

inline int flock( int fd, int operation ){
  return lockf( fd, operation, 0 );	
}
#endif

● 高水準ファイル入出力でのファイルロック

ファイルを普通に開く操作を行った直後に次のようにして 排他的ロックを掛けます。

#include <fcntl.h>

FILE* fptr = fopen("file", "w" );
flock( fileno(fptr), LOCK_EX );   // BSD系のUNIXの場合
lockf( fileno(fptr), F_LOCK, 0 ); // 他のUNIXの場合

同時に並行して走っている複数のプロセスのうちの ひとつがこのようにしてファイルに排他的ロックを掛けると、 そのプロセスは通常どおりにその後の作業を行いますが、 他のプロセスが遅れてこの lockf()を実行すると、 この排他的ロックが掛かっている間はそのプロセスは停止されます。 ロックを掛けたプロセスがロックを解除するかファイルを閉じると、 lockf()で停止していた他のプロセスは、今度はそのプロセスが 排他的ロックを掛けてその後の作業を行います。

ロックを明示的に解除するには次のようにします。

flock( fileno(fptr), LOCK_UN );    // BSD系のUNIXの場合
lockf( fileno(fptr), F_ULOCK, 0 ); // 他のUNIXの場合

ロックを解除するには単に fclose(fptr) でも構いません。


● 低水準ファイル入出力でのファイルロック

ファイル記述子 fd に対して、lockf()を実行します。

flock( fd, LOCK_EX );    // BSD系のUNIXの場合
lockf( fd, F_LOCK, 0 );  // 他のUNIXの場合

基本的に高水準と同じです。

BSD系 UNIXではより簡単に排他的ロックを掛けることができます。 open()関数の実行時に O_EXLOCK を追加します。

fd = open( "file", O_RDWR|O_EXLOCK );
これだけでこのファイルはロックされました。このロックは ファイルを閉じると解除されます。


目次

Copyright(C) by Naoki Watanabe. Oct 21st, 1995.
渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp