`LOCK_EX` запрещает чтение, но не запись?

Почему я не могу прочитать файл заблокированный с LOCK_EX? Я все еще могу написать ему.

Я хотел знать, что происходит, если один процесс блокирует файл (с LOCK_SH или же LOCK_EX) и другой процесс пытается прочитать или записать этот файл, но игнорирует блокировку вообще. Итак, я сделал небольшой скрипт, который имеет 3 функции:

  • Блокировка: открывает целевой файл, записывает в него, блокирует файл (с указанной блокировкой), записывает в него снова, спит 10 секунд, разблокирует и закрывает его.
  • Чтение: открывает целевой файл, читает из него и закрывает его.
  • Запись: открывает целевой файл, пишет в него и закрывает его.

Я проверил это, имея две консоли рядом и выполняя следующие действия:

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";
}
?>

1

Решение

Это очень хороший вопрос, но также и сложный, потому что он зависит от множества условий.

Мы должны начать с другой пары типов блокировки — рекомендательной и обязательной:

  • Консультативная блокировка просто дает вам «флаги состояния», с помощью которых вы знать, заблокирован ли ресурс или нет.
  • Обязательная блокировка Применяет блокировки, независимо от того, проверяете ли вы эти «флаги состояния».

… и это должно ответить на ваш вопрос, но я продолжу, чтобы объяснить ваш конкретный случай.

Вы, похоже, испытываете поведение консультативных блокировок — ничто не мешает вам читать или записывать в файл, независимо от того, есть ли для него блокировка или вы даже проверили ее.
Тем не менее, вы найдете обратите внимание в руководстве по PHP для flock (), говоря следующее:

flock () использует обязательную блокировку вместо рекомендательной блокировки в Windows. Обязательная блокировка также поддерживается в операционных системах на основе Linux и System V с помощью обычного механизма, поддерживаемого системным вызовом fcntl (): если в рассматриваемом файле установлен бит разрешения setgid и очищен бит выполнения группы. В Linux файловая система также должна быть смонтирована с опцией mand, чтобы это работало.

Так что, если PHP использует обязательную блокировку в Windows, и вы проверили это в Windows, либо руководство является неправильным / устаревшим / неточным (мне лень проверять это прямо сейчас) или же Вы должны прочитать это большое красное предупреждение на той же странице:

В некоторых операционных системах flock () реализован на уровне процесса. При использовании многопоточного серверного API, такого как ISAPI, вы не сможете полагаться на flock () для защиты файлов от других сценариев PHP, работающих в параллельных потоках одного и того же экземпляра сервера!

flock () не поддерживается в устаревших файловых системах, таких как FAT и его производные, и поэтому всегда будет возвращать FALSE в этой среде (это особенно актуально для пользователей Windows 98).

Я не верю, что даже возможно, что ваш исполняемый файл php-cli как-то порождает потоки для себя, поэтому есть вариант, что вы используете файловую систему, которая просто не поддерживает блокировку.

Я думаю, что руководство не совсем точно, и вы действительно получаете консультативный блокировки в Windows, потому что вы также испытываете другое поведение между LOCK_EX (эксклюзивный замок) и LOCK_SH (общая блокировка) — для них не имеет смысла различаться, если ваша файловая система просто игнорирует блокировки.

И это подводит нас к разнице между эксклюзивный а также общий замки или LOCK_EX а также LOCK_SH соответственно. Логика обоих основывается на пишу, но есть небольшая разница …

  • Исключительные блокировки (как правило) используются, когда вы хотите записать в файл, потому что только один процесс одновременно может удерживать исключительную блокировку для одного и того же ресурса. Это дает вам безопасность, потому что никакой другой процесс не будет читать с этого ресурса, пока владелец блокировки пишет в него.
  • Общие блокировки используются для гарантии того, что ресурс не записывается в пока ты читаешь с него. Поскольку ни один процесс не записывает данные в ресурс, он не изменяется и поэтому безопасен для чтения для нескольких процессов одновременно.
3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]