ファイルロック

UNIXでは複数のプロセスが同時にファイルにアクセスできるが、自動的にはロックされないので、おそらく最後のプロセスが書き込んだ結果が結果的に残こることになる。これでは都合が悪いので(特に分散ファイルシステム分散OSでは)、ファイルロックという仕組みが提供されている。UNIXではflockやlockf、fcntlが使われる。UNIXのファイルロックは基本的にアドバイザリロック(advisory lock)と言って、プログラムがお行儀よく書かれていることが前提になっている。一方、カーネルによって強制的にファイルアクセスを排他制御する仕組みを強制ロック(mandatory lock)と呼ぶ。

Plan 9には強制ロックを実現する仕組みとしてファイルパーミッションを拡張している。exclusive (l)ビットがそれである。

% touch a
% chmod +l a
% ls -l a
-lrw-rw-r-- M 8 oraccha oraccha 0 Feb 21 11:02 a
% cat > a

とやって、別プロセスからファイルaをcatしようとすると、エラーになる。

% cat a
cat: can't open a: 'a' exclusive lock

もちろんUNIXでも強制ロックをサポートしている。例えば、SVRではgroup executiveビットを落として、sguidビットを立てる(これは実質的に意味がない組み合わせ)と強制ロックが有効になるらしいが、Plan 9の方がより直感的である。

ここまではファイル全体をロックする場合の話だけど、ファイルの一部だけロックする、つまりrange lockはどうサポートされているのだろうか?

さて、lsの結果を見て鋭い人はパーミッションのMSBが何か気になったかもしれないが、追記のみ可能なファイルを実現するためのappend (a)ビットに割り当てられている。aビットが立っている場合は、シェルから">>"の代わりに">"でリダイレクトしても追記になる。

% touch b
% chmod +l b
% ls -l b
a-rw-rw-r-- M 8 oraccha oraccha 0 Feb 21 11:03 b
% echo hello > b
% echo world > b
% cat b
hello
world

例えば、ログ関係のファイル(/sys/log)にはappend onlyビットが付いている。Plan 9にはsyslogデーモンは存在せず、個々のプログラムがログファイルに書き出している。メールスプールなどはappend onlyでかつ、読み書きで排他が必要なので、パーミッションは"alrw--w--w-"になっている。

さらにパーミッションがらみで言うと、Plan 9にsuidやstickyビットは存在しない。

補足:@takeokaさんから突っ込みが入ったが、元々UNIXにはロックに使えるようなアトミックな操作にはopen(O_CREAT|O_EXCL)しかなかったので、これで/tmpにロックファイルを作って排他制御していた。NFSではO_CREAT|O_EXCLが動かないので、別の仕掛けが必要になったという経緯があったようだ。

Plan 9で同様な操作を書く場合はcreate(OEXCL)だ。Plan 9にはO_CREATに相当するフラグがないのでcreate(2)を使う*1

*1:今時のUNIXではcreatはシステムコールじゃなくてライブラリ関数なんだろうけど。