web-dev-qa-db-ja.com

読み取りと書き込みの両方のLOCK_EXはアトミックである必要がありますか?

file_put_contents ( "file", "data", LOCK_EX )

書き込み用(つまり、ロックを取得して書き込み)

file_get_contents ( "file", LOCK_EX )

読み取り用(つまり、ロックを取得してから読み取ります)

例外をスローしますか?エラーを発生させますか?ロックが取得されるまでブロックしますか?または少なくとも-すべき? phpがいつかこのように動作する可能性はありますか?

編集:私は名前の変更を使用することが可能であることを知っています-私はこれに対する答えを知りたいです...

22
Kamil Tomšík

この答えは長いので、要約は次のとおりです。いいえ、file_get_contents()はアドバイザリロックを尊重しないため、アトミックではありません。

PHPのファイルロックについて:

PHPでは、* nixプラットフォームでは、ファイルシステムのロックは助言のみです。あたり ドキュメント (エンファシスマイン):

PHPは、アドバイザリな方法で完全なファイルをロックするポータブルな方法をサポートしています(つまり、すべてのアクセスプログラムは同じロック方法を使用する必要があります。そうしないと機能しません )。デフォルトでは、この関数は要求されたロックが取得されるまでブロックします。これは、以下に記載されているLOCK_NBオプションを使用して(Windows以外のプラットフォームで)制御できます。

したがって、ファイルにアクセスしているすべてのプロセスがこのロック方法を使用している限り、問題はありません。

ただし、正常なWebサーバーを使用して静的HTMLファイルを作成している場合、ロックは無視されます。書き込みの途中で、リクエストが届くと、Apacheは部分的に書き込まれたファイルを提供します。ロックは、ロックを読み取る他のプロセスには影響しません。

唯一の実際の例外は、ファイルシステムで_-o mand_の特別なマウントオプションを使用して必須ロックを有効にする場合です(ただし、これはあまり使用されておらず、パフォーマンスが低下する可能性があります)。

詳細については、 ファイルロック をお読みください。つまり、Unixの下のセクション:

つまり、協調するプロセスはロックを使用してファイルへのアクセスを調整できますが、プログラムはロックを無視して、任意の方法でファイルにアクセスすることもできます。

したがって、結論として、_LOCK_EX_を使用すると、ファイルにアドバイザリロックが作成されます。ファイルを読み取ろうとすると、リーダーがロックを尊重および/またはチェックした場合にのみブロックされます。そうでない場合、ロックは無視されます(可能性があるため)。

やってみて。 1つのプロセスで:

_file_put_contents('test.txt', 'Foo bar');
$f = fopen('test.txt', 'a+');
if (flock($f, LOCK_EX)) {
    sleep(10);
    fseek($f, 0);
    var_dump(fgets($f, 4048));
    flock($f, LOCK_UN);
}
fclose($f);
_

そして、それが眠っている間、これを呼んでください:

_$f = fopen('test.txt', 'a+');
fwrite($f, 'foobar');
fclose($f);
_

出力はfoobar ..になります。

具体的には_file_get_contents_について:

他の特定の質問に対して、最初に、 _LOCK_EX_ への_file_get_contents_パラメーターはありません。だからあなたはそれを渡すことはできません。

ここで、 ソースコード を見ると、521行目で定義されている関数_file_get_contents_がわかります。file_put_contents('file', 'txt', LOCK_EX);を渡すときのように、内部関数_php_stream_lock_への呼び出しはありません。同じファイルの589行目で定義されています。

それでは、それをテストしましょう。

File1.php内:

_file_put_contents('test.txt', 'Foo bar');
$f = fopen('test.txt', 'a+');
if (flock($f, LOCK_EX)) {
    sleep(10);
    fseek($f, 0);
    var_dump(fgets($f, 4048));
    flock($f, LOCK_UN);
}
fclose($f);
_

File2.phpの場合:

_var_dump(file_get_contents('test.txt'));
_

実行すると、_file2.php_はすぐに戻ります。いいえ、_file_get_contents_がファイルロックをまったく尊重していないようです...

44
ircmaxell

理論の質問は、 プログラマー でここよりもはるかにうまく機能します。

この時点で、PHPはアトミックファイルロックをサポートしていません。

簡単に言えば、PHPは fopenflock の組み合わせ操作をサポートしていないので、プロセスがファイルをロックする前に、プロセスが開いたファイルを別のプロセスがロックする機会は常に小さなウィンドウになります。

そうは言っても、 flock は、デフォルトで、ロックが解除されるまでブロックします。ただし、Linux/BSDでのアドバイザリロックに関するircmaxellのメモに注意してください。

注:読み取りプロセスでは、複数のリーダースレッドが同時にロックできるように、LOCK_SHではなくLOCK_EXにすることができます。書き込みは常にLOCK_EXを使用して行う必要があります。そうしないと、データが破損するリスクがあります。

注2:前の注は、排他ロックが存在しない場合にのみ共有ロックを取得できるため機能しますが、排他ロックでは、ロックを取得する前に、いかなる種類のロックも存在しない必要があります。

3
Powerlord