0

PHP群れのドキュメントからの引用:

flock()はファイル ポインタを必要とするため、(fopen() への "w" または "w+" 引数を使用して) 書き込みモードでファイルを開くことによって切り捨てようとするファイルへのアクセスを保護するために、特別なロック ファイルを使用する必要がある場合があります。 )。

PHP fopen ドキュメントからの引用:

'c' -- ファイルを書き込み専用に開きます。ファイルが存在しない場合は、作成されます。存在する場合、( 'w'とは対照的に) 切り捨てられることも、この関数の呼び出しが失敗することもありません ( 'x'の場合と同様)。ファイル ポインタは、ファイルの先頭に配置されます。'w'を使用すると、ロックが取得される前にファイルが切り捨てられる可能性があるため (切り捨てが必要な場合は、ftruncate() をロックが要求された後に使用されます)。

ファイルをinモードflockで開いて切り捨てた後にアドバイザリ ロックを取得する際の問題を理解しようとしています。書き込みモードで開いて切り捨てようとしているファイルへのアクセスを保護するために、特別なロック ファイルを使用したいシナリオは何ですか?fopen'w'

4

1 に答える 1

5

質問を入力しているうちに、この質問に対する答えに気づきましたので、私の答えも投稿します。別のリーダー スクリプトがファイルを読み取ろうとしているときに、ファイルを切り捨てた後にファイルのアドバイザリ ロックを取得すると、問題が発生する可能性があります。'w'ライター スクリプトがモードでファイルを開いてからファイルのロックを取得するまでの間にファイルを読み取った場合、リーダー スクリプトは切り捨てられたファイル (空のファイル) に遭遇します。

この問題を示す 2 つのスクリプトを次に示します。最初のスクリプトは、その PID を foo.txt というファイルに書き込みます。2 番目のスクリプトは、このファイルから PID を読み取ろうとします。

write.php:

<?php
$f = fopen('foo.txt', 'w');

sleep(5); // Artificial delay between open and lock

flock($f, LOCK_EX);
fwrite($f, getmypid() . "\n");
flock($f, LOCK_UN);
fclose($f);
?>

read.php:

<?php
$f = fopen('foo.txt', 'r');
flock($f, LOCK_EX);

$size = filesize('foo.txt');
echo ($size === 0 ? "File is empty\n" : fread($f, $size));

flock($f, LOCK_UN);
fclose($f);
?>

次のシェル セッションは、write.php がファイルを開いた後、write.php がファイルのロックを取得する前に、read.php がファイルを読み込もうとしたときに空のファイルを見つけることを示しています。

$ php write.php < /dev/null &
[1] 17511
$ for i in {1..10}; do php read.php; sleep 1; done
File is empty
File is empty
File is empty
File is empty
File is empty
[1]+  Done                    php write.php < /dev/null
17511
17511
17511
17511
17511

この問題は、ファイルが切り捨てられた後にファイルのロックを取得するために発生します。それは少し遅すぎます。最初にロックを取得してから、切り捨てやその他の変更を実行します。これには 2 つの方法があります。

特別なロックファイルを使用する

write2.php:

<?php
$lock = fopen('foo.lock', 'w');

sleep(5); // Artificial delay between open and lock

flock($lock, LOCK_EX);
$f = fopen('foo.txt', 'w');
fwrite($f, getmypid() . "\n");
fclose($f);
flock($lock, LOCK_UN);
?>

次のシェル セッションは、read.php が切り捨てられたファイルに遭遇したことがないことを示しています。

$ php write2.php < /dev/null &
[1] 17533
$ for i in {1..10}; do php read.php; sleep 1; done
17511
17511
17511
17511
17511
[1]+  Done                    php write2.php < /dev/null
17533
17533
17533
17533
17533

モードでファイルを開き'c'、ロックする

write3.php:

<?php
$f = fopen('foo.txt', 'c');

sleep(5); // Artificial delay between open and lock

flock($f, LOCK_EX);
ftruncate($f, 0);
fwrite($f, getmypid() . "\n");
flock($f, LOCK_UN);

fclose($f);
?>

このスクリプトは、ファイルをモードで開いても'c'ファイルが自動的に切り捨てられないという事実を利用しているためftruncate、ロックを取得した後、書き込み前にファイルを切り捨てることができます。その結果、read.php が切り捨てられたファイルに遭遇することはありません。

$ php write3.php < /dev/null &
[1] 17558
$ for i in {1..10}; do php read.php; sleep 1; done
17533
17533
17533
17533
17533
[1]+  Done                    php write3.php < /dev/null
17558
17558
17558
17558
17558
于 2013-11-03T19:47:22.897 に答える