因为 flock()需要文件指针,所以您可能必须使用特殊的 锁定文件以保护对您要截断的文件的访问 以写模式打开它(fopen()带有“w”或“w +”参数)。
'c' - 打开文件仅供写入。如果该文件不存在,则为 创建。如果它存在,它既不会被截断(而不是'w'), 也不会调用此函数失败(如'x'的情况)。该 文件指针位于文件的开头。这可能是 如果希望获得咨询锁(请参阅flock())之前有用 尝试修改文件,因为使用'w'可能会截断文件 在获得锁之前(如果需要截断,则为ftruncate() 可以在请求锁定后使用。)
我试图通过在flock
模式下使用fopen
打开文件来截断文件后,通过'w'
获取咨询锁定的问题。在什么情况下,人们会想要使用一个特殊的锁文件来保护对一个打算在写入模式下打开它的文件的访问?
答案 0 :(得分:5)
我在输入问题时意识到了这个问题的答案,所以
我也会发布我的答案。之后获取文件的建议锁定
当另一个读者脚本试图读取时,截断它可能是一个问题
文件。 reader脚本会遇到截断的文件(空文件)
如果碰巧在写作者之间读取文件
脚本以'w'
模式及其获取的时间打开文件
锁定文件。
以下是演示此问题的两个脚本。第一个脚本写道 将其PID转换为名为foo.txt的文件。第二个脚本试图阅读 来自该文件的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);
?>
以下shell会话显示read.php找到一个空文件 当它在write.php打开文件后尝试读取文件时 在write.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
出现此问题是因为我们在文件被截断后获取了锁定。那有点太晚了。我们想先获取一个锁,然后对其执行截断或任何其他修改。有两种方法可以做到这一点。
<强> 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);
?>
以下shell会话显示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