`LOCK_EX`禁止读,不禁止写?

`LOCK_EX` prohibits reading, but not writing?

为什么我不能读取用 LOCK_EX锁定的文件?我仍然可以写入它。

我想知道,如果一个进程锁定了一个文件(使用 LOCK_SHLOCK_EX)而另一个进程试图读取或写入该文件,但忽略了锁定,会发生什么根本。所以我制作了一个具有 3 个功能的小脚本:

我通过并排放置两个控制台并执行以下操作对其进行了测试:

FIRST CONSOLE                |        SECOND CONSOLE
-----------------------------+-----------------------
php test lock LOCK_SH        |        php test read
php test lock LOCK_SH        |        php test write
php test lock LOCK_EX        |        php test read
php test lock LOCK_EX        |        php test write

LOCK_SH好像一点作用都没有,因为第一个进程和第二个进程一样可以读写文件。如果文件被第一个进程用 LOCK_EX 锁定,两个进程仍然可以写入它,但只有第一个进程可以读取。 这背后有什么道理吗?

这是我的小测试程序(在 Windows 7 Home Premium 64 位上测试):

<?php
    // USAGE: php test [lock | read | write] [LOCK_SH | LOCK_EX]
    // The first argument specifies whether
    //   this script should lock the file, read
    //   from it or write to it.
    // The second argument is only used in lock-mode
    //   and specifies whether LOCK_SH or LOCK_EX
    //   should be used to lock the file


    // Reads $file and logs information.
    function r ($file) {
        echo "Reading file\n";
        if (($buffer = @fread($file, 64)) !== false)
            echo "Read ", strlen($buffer), " bytes: ", $buffer, "\n";
        else
            echo "Could not read file\n";
    }

    // Sets the cursor to 0.
    function resetCursor ($file) {
        echo "Resetting cursor\n", @fseek($file, 0, SEEK_SET) === 0 ? "Reset cursor" : "Could not reset cursor", "\n";
    }

    // Writes $str to $file and logs information.
    function w ($file, $str) {
        echo "Writing \"", $str, "\"\n";
        if (($bytes = @fwrite($file, $str)) !== false)
            echo "Wrote ", $bytes, " bytes\n";
        else
            echo "Could not write to file\n";
    }

    // "ENTRYPOINT"
    if (($file = @fopen("check", "a+")) !== false) {
        echo "Opened file\n";

        switch ($argv[1]) {
        case "lock":
            w($file, "1");

            echo "Locking file\n";
            if (@flock($file, constant($argv[2]))) {
                echo "Locked file\n";

                w($file, "2");
                resetCursor($file);
                r($file);

                echo "Sleeping 10 seconds\n";
                sleep(10);
                echo "Woke up\n";

                echo "Unlocking file\n", @flock($file, LOCK_UN) ? "Unlocked file" : "Could not unlock file", "\n";
            } else {
                echo "Could not lock file\n";
            }

            break;

        case "read":
            resetCursor($file);
            r($file);
            break;

        case "write":
            w($file, "3");
            break;
        }

        echo "Closing file\n", @fclose($file) ? "Closed file" : "Could not close file", "\n";
    } else {
        echo "Could not open file\n";
    }
?>

这是一个很好的问题,但也是一个复杂的问题,因为它取决于很多条件。

我们必须从另一对锁定类型开始——咨询型和强制型:

  • 建议锁定只是给你 "status flags" 知道资源是否被锁定。
  • 强制锁定强制锁定,无论您是否正在检查这些"status flags"。

...这应该可以回答您的问题,但我会继续解释您的具体情况。

您似乎遇到的是咨询锁的行为 - 没有什么可以阻止您读取或写入文件,无论是否有锁或者您是否检查过。
然而,你会发现一个note in the PHP manual for flock(),上面写着:

flock() uses mandatory locking instead of advisory locking on Windows. Mandatory locking is also supported on Linux and System V based operating systems via the usual mechanism supported by the fcntl() system call: that is, if the file in question has the setgid permission bit set and the group execution bit cleared. On Linux, the file system will also need to be mounted with the mand option for this to work.

因此,如果 PHP 在 Windows 上使用强制锁定并且您已经在 Windows 上测试过它,那么手册是 wrong/outdated/inaccurate(我太懒了现在检查)你必须阅读同一页上的这个大红色警告:

On some operating systems flock() is implemented at the process level. When using a multithreaded server API like ISAPI you may not be able to rely on flock() to protect files against other PHP scripts running in parallel threads of the same server instance!

flock() is not supported on antiquated filesystems like FAT and its derivates and will therefore always return FALSE under this environments (this is especially true for Windows 98 users).

我认为您的 php-cli 可执行文件甚至不可能以某种方式为自己生成线程,因此您可以选择使用不支持锁定的文件系统。

我的猜测是该手册并不完全准确,您实际上确实在 Windows 上获得了 建议 锁定,因为您也遇到了不同的行为LOCK_EX(独占锁)和 LOCK_SH(共享锁)——如果您的文件系统忽略锁,那么它们之间的区别就没有意义。

这给我们带来了独占共享锁或LOCK_EXLOCK_SH之间的区别.两者背后的逻辑都是基于写作,但略有不同...

  • 当您要写入文件时(通常)使用排他锁,因为一次只有一个进程可能持有对同一资源的排他锁。这给了你安全,因为当锁持有者写入它时,没有其他进程会从该资源读取。
  • 共享锁用于确保在您读取资源时不会写入资源。由于没有进程正在写入该资源,因此不会对其进行修改,因此可以安全地同时供多个进程读取。