質問を入力しているうちに、この質問に対する答えに気づきましたので、私の答えも投稿します。別のリーダー スクリプトがファイルを読み取ろうとしているときに、ファイルを切り捨てた後にファイルのアドバイザリ ロックを取得すると、問題が発生する可能性があります。'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