0

引用PHP 群文档

因为flock()需要一个文件指针,所以您可能必须使用一个特殊的锁定文件来保护对您打算通过以写模式打开它来截断的文件的访问(在fopen() 中使用“w”或“w+”参数) )。

引用PHP fopen 文档

'c' -- 打开文件仅用于写入。如果文件不存在,则创建它。如果它存在,它既不会被截断(与'w'相对),也不会调用此函数失败(如'x'的情况)。文件指针位于文件的开头。如果希望在尝试修改文件之前获得建议锁(请参阅flock()),这可能很有用,因为使用'w'可以在获得锁之前截断文件(如果需要截断,可以使用 ftruncate()在请求锁定后使用)。

我试图了解通过在模式下flock打开文件被截断后获得咨询锁的问题。在什么情况下,人们会想要使用特殊的锁定文件来保护对想要通过以写模式打开来截断的文件的访问?fopen'w'

4

1 回答 1

5

我在输入问题时意识到了这个问题的答案,所以我也会发布我的答案。当另一个阅读器脚本试图读取文件时,在截断文件后获取文件的建议锁定可能会成为问题。'w'如果阅读器脚本碰巧在编写器脚本以模式打开文件的时间和它获取文件锁定的时间之间读取文件,则阅读器脚本将遇到截断的文件(空文件) 。

这是演示该问题的两个脚本。第一个脚本将其 PID 写入名为 foo.txt 的文件中。第二个脚本尝试从此文件中读取 PID。

写.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);
?>

读取.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
于 2013-11-03T19:47:22.897 回答