PHP и запись в файл. Помогите.

R
На сайте с 13.06.2009
Offline
47
693

Помогите разобраться с блокировками файлов.

Вообщим несколько процессов работают с одним файлом суть работы такова: сначала читаем, потом работаем с прочитанным - меняем его и записываем обратно. Также требуется актуальность информации в файле. Т.е. если первый процесс обновил файл, то второй должен получить именно обновленный вариант.

Добиться этого я не могу. Пробую пока на локалке (WIN 7; файловая система NTFS). Запускается 4-ре процесса и обязательно сколько не то да не выполнится (вылетает ошибка потому что процесс получает из файла не то что надо).

Вот пример функции.


function saveData()
{
$filename = ...;
$fh = fopen($filename, "r+");
if ($fh) {
flock($fh, LOCK_EX);
$contents = fread($fh, filesize($filename));
// Работаю с данными. В частности сливаю массив полученный из файла с
// с массивом полученным в скрипте.
ftruncate($fh, 0);
fseek($fh, 0, SEEK_SET);
fwrite($fh, $contents);
fflush($fh);
flock($fh, LOCK_UN);
fclose($fh);
}else {
throw new Exception('Не удалось получить доступ к файлу.');
}
}

Вот приблизительно так. Ставлю исключительную блокировку и как я понимаю после этого не кто не должен лапать файл (читать или писать) пока с него не снимется эта исключительная блокировка.

Чего делаю не так и как поправить? Может дело в Windows?

Таггу x_x
На сайте с 31.10.2005
Offline
445
#1
recrut:
Добиться этого я не могу. Пробую пока на локалке (WIN 7; файловая система NTFS)

Это невероятно неправильное решение для отладки чего-бы то нибыло, связанного с PHP. А уж для работы с файлами это былинный провал, батенька.

☠️☠️☠️
F
На сайте с 24.04.2009
Offline
45
#2
recrut:
Может дело в Windows?

Дело в файловой системе.

R
На сайте с 13.06.2009
Offline
47
#3
Fearful:
Дело в файловой системе.

Из доки

flock() не будет работать на NFS и многих других сетевых файловых системах. Обратитесь к документации вашей операционной системы для получения дополнительной информации.

Под NFS подразумевается NTFS, что ли? Я думал это что то другое.

А обойти это можно как не то, заставить работать на винде?

Таггу x_x
На сайте с 31.10.2005
Offline
445
#4
recrut:
работать на винде?

Говорю глаголами: на винде не нужно работать, 99% из того что у вас на винде сработает не будет больше нигде работать так, как вы задумали. Вы чего вообще? Какая винда, зачем? :)

[Удален]
#5

Tarry, денвер, гули

bearman добавил 03.06.2010 в 23:57

bool flock ( resource $handle , int $operation [, int &$wouldblock ] )

http://ru2.php.net/flock

и как надо той функцией пользоваться батенька?

правильно, вот так (код из примера сразу после доки)

//code here
while (!flock($file_handle, LOCK_EX | LOCK_NB)) {
//Lock not acquired, try again in:
usleep(round(rand(0, 100)*1000)) //0-100 miliseconds
}
BrokenBrake
На сайте с 03.03.2007
Offline
194
#6
Tarry:
Говорю глаголами: на винде не нужно работать, 99% из того что у вас на винде сработает не будет больше нигде работать так, как вы задумали.

В данном случае как раз наоборот, на Win не будет работать то, что сработает на UNIX-like системах.

T.R.O.N
На сайте с 18.05.2004
Offline
314
#7
recrut:
Ставлю исключительную блокировку и как я понимаю после этого не кто не должен лапать файл (читать или писать) пока с него не снимется эта исключительная блокировка.

Для Win это действие не имеет смысла. там иные механизмы. Чтобы работало на винде, вместо блокировок используйте проверку на занятость файла (если она сообще здесь имеет смысл.)

пых как был потомком перла, так и перенес в себя все особенности и ограничения.

От воздержания пока никто не умер. Хотя никто и не родился! Prototype.js был написан теми, кто не знает JavaScript, для тех, кто не знает JavaScript (Richard Cornford)
R
На сайте с 13.06.2009
Offline
47
#8

Вот на php.net в коментах есть вариант с созданием временного файла.


function mLock($lockId, $acquire = null) {
static $file_format = '/%s.lock';
static $handlers = array();

if (is_bool($acquire)) {
$file = sprintf($file_format, md5($lockId), $lockId);
}

if ($acquire === FALSE) {
if (isset($handlers[$lockId])) {
@fclose($handlers[$lockId]);
@unlink($file);
unset($handlers[$lockId]);
}else {
trigger_error("Lock '$lockId' is already unlocked", E_USER_WARNING);
}
}

if ($acquire === TRUE) {
if (!isset($handlers[$lockId])) {
$handler = FALSE;
$count = 100;
do {
if (!file_exists($file) || @unlink($file)) {
$handler = @fopen($file, "x");
}
if (FALSE === $handler) {
sleep(1);
}else {
$handlers[$lockId] = $handler;
}
}while (FALSE === $handler && $count-- > 0);
}else {
trigger_error("Lock '$lockId' is already locked", E_USER_WARNING);
}
}

return isset($handlers[$lockId]);
}

Вроде как работает все пишется без как положено. Но меня смущает вот этот момент


if (!file_exists($file) || @unlink($file)) {
$handler = @fopen($file, "x");
}

Т.е. выходит, что если файл открылся, то мы его удаляем (TRUE) и идем создавать, а если он не открылся, то опять (TRUE) и идем создавать.

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

Но вариант вроде нормальный. Я вот подумал, а то если проверять последнее время доступа, и если оно больше скажем (N сек.), то значит какой-то процесс завершился с ошибкой (не снял блокировку).

Ща, попробую. Потом отпишу.

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий