Подтвердить что ты не робот

Как заблокировать файл в Perl?

Каков наилучший способ создания блокировки файла в Perl?

Лучше ли вам ставить файл или создать файл блокировки для блокировки и проверить блокировку файла блокировки?

4b9b3361

Ответ 1

Если вы в конечном итоге используете стадо, вот какой код для этого:

use Fcntl ':flock'; # Import LOCK_* constants

# We will use this file path in error messages and function calls.
# Don't type it out more than once in your code.  Use a variable.
my $file = '/path/to/some/file';

# Open the file for appending.  Note the file path is quoted
# in the error message.  This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, '>>', $file) or die "Could not open '$file' - $!";

# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX) or die "Could not lock '$file' - $!";

# Do something with the file here...

# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above.  This could create
# a race condition.  The close() call below will unlock the
# file for you, but only after writing any buffered data.

# In a world of buffered i/o, some or all of your data may not 
# be written until close() completes.  Always, always, ALWAYS 
# check the return value of close() if you wrote to the file!
close($fh) or die "Could not write '$file' - $!";

Некоторые полезные ссылки:

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

Ответ 2

Другие ответы охватывают блокировку Perl flock довольно хорошо, но во многих системах Unix/Linux существуют две независимые системы блокировки: блокировки BSD flock() и POSIX fcntl().

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

В таких случаях вы можете посмотреть File:: FcntlLock или File:: lockf, Также возможно сделать блокировку на основе fcntl() в чистом Perl (с некоторыми волосатыми и не переносимыми битами пакета()).

Быстрый обзор различий flock/fcntl/lockf:

lockf почти всегда реализуется поверх fcntl, имеет только блокировку на уровне файлов. Если реализовано с использованием fcntl, ограничения ниже также применяются к lockf.

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

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

Иногда (системы SYSV) стая эмулируется с помощью lockf или fcntl; на некоторых BSD-системах lockf эмулируется с использованием стаи. Как правило, эти виды эмуляции работают плохо, и вам рекомендуется избегать их.

Ответ 4

Ryan P писал (а):

В этом случае файл фактически разблокируется в течение короткого периода времени, когда файл снова открывается.

Так что не делай этого. Вместо этого open файл для чтения/записи:

open my $fh, '+<', 'test.dat'
    or die "Couldn’t open test.dat: $!\n";

Когда вы готовы написать счетчик, просто seek вернитесь к началу файла. Обратите внимание: если вы это сделаете, вы должны truncate перед close, так что файл не останется с завершающим мусором, если его новое содержимое короче, чем предыдущие. (Обычно текущая позиция в файле находится в конце, поэтому вы можете просто написать truncate $fh, tell $fh.)

Также обратите внимание, что я использовал три аргумента open и лексический дескриптор файла, а также проверил успешность операции. Пожалуйста, избегайте использования глобальных дескрипторов файлов (глобальные переменные являются плохими, mmkay?) И magic two-argument open (который был источником многих (n) уязвимостей в коде Perl), и всегда проверяйте, достиг ли ваш open.

Ответ 5

Я думаю, было бы гораздо лучше показать это с помощью лексических переменных в качестве обработчиков файлов и обработка ошибок. Также лучше использовать константы из модуля Fcntl, чем жесткий код, магическое число 2, которое не может быть правильным числом во всех операционных системах.

    use Fcntl ':flock'; # import LOCK_* constants

    # open the file for appending
    open (my $fh, '>>', 'test.dat') or die $!;

    # try to lock the file exclusively, will wait till you get the lock
    flock($fh, LOCK_EX);

    # do something with the file here (print to it in our case)

    # actually you should not unlock the file
    # close the file will unlock it
    close($fh) or warn "Could not close file $!";

Просмотрите полную документацию о стаде и Блокировка файла tutorial на PerlMonks, хотя это также использует старый стиль использования дескриптора файла.

На самом деле я обычно пропускаю обработку ошибок при закрытии(), поскольку нет я могу сделать, если он все равно не работает.

Что касается блокировки, если вы работаете в одном файле, тогда заблокируйте этот файл. Если вам нужно заблокировать сразу несколько файлов, то - чтобы избежать мертвых блокировок - лучше выбрать один файл, который вы блокируете. Не имеет значения, является ли это одним из нескольких файлов, которые вам действительно нужно заблокировать, или отдельного файла, который вы создаете, только для цели блокировки.

Ответ 6

Рассматривали ли вы использование LockFile:: Simple module? Это делает большую часть работы для вас уже.

В моем прошлом опыте я нашел его очень простым в использовании и прочным.

Ответ 7

use strict;

use Fcntl ':flock'; # Import LOCK_* constants

# We will use this file path in error messages and function calls.
# Don't type it out more than once in your code.  Use a variable.
my $file = '/path/to/some/file';

# Open the file for appending.  Note the file path is in quoted
# in the error message.  This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, '>>', $file) or die "Could not open '$file' - $!";

# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX);


# Do something with the file here...


# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above.  This could create
# a race condition.  The close() call below will unlock it
# for you, but only after writing any buffered data.

# In a world of buffered i/o, some or all of your data will not 
# be written until close() completes.  Always, always, ALWAYS 
# check the return value on close()!
close($fh) or die "Could not write '$file' - $!";

Ответ 8

Моя цель в этом вопросе состояла в том, чтобы заблокировать файл, используемый в качестве хранилища данных для нескольких сценариев. В конце я использовал аналогичный код для следующего (от Криса):

open (FILE, '>>', test.dat') ; # open the file 
flock FILE, 2; # try to lock the file 
# do something with the file here 
close(FILE); # close the file

В своем примере я удалил ФАЙЛ стада, 8, так как закрытие (FILE) также выполняет это действие. Реальная проблема заключалась в том, что при запуске script он должен удерживать текущий счетчик, а когда он заканчивается, он должен обновить счетчик. Здесь Perl имеет проблему, чтобы прочитать файл:

 open (FILE, '<', test.dat');
 flock FILE, 2;

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

 open (FILE, '>', test.dat'); #single arrow truncates double appends
 flock FILE, 2;

В этом случае файл фактически разблокируется в течение короткого периода времени, когда файл снова открывается. Это демонстрирует случай для файла внешней блокировки. Если вы собираетесь изменять контексты файла, используйте файл блокировки. Измененный код:

open (LOCK_FILE, '<', test.dat.lock') or die "Could not obtain lock";
flock LOCK_FILE, 2;
open (FILE, '<', test.dat') or die "Could not open file";
# read file
# ...
open (FILE, '>', test.dat') or die "Could not reopen file";
#write file
close (FILE);
close (LOCK_FILE);

Ответ 9

Разработано http://metacpan.org/pod/File::FcntlLock

use Fcntl qw(:DEFAULT :flock :seek :Fcompat);
use File::FcntlLock;
sub acquire_lock {
  my $fn = shift;
  my $justPrint = shift || 0;
  confess "Too many args" if defined shift;
  confess "Not enough args" if !defined $justPrint;

  my $rv = TRUE;
  my $fh;
  sysopen($fh, $fn, O_RDWR | O_CREAT) or LOGDIE "failed to open: $fn: $!";
  $fh->autoflush(1);
  ALWAYS "acquiring lock: $fn";
  my $fs = new File::FcntlLock;
  $fs->l_type( F_WRLCK );
  $fs->l_whence( SEEK_SET );
  $fs->l_start( 0 );
  $fs->lock( $fh, F_SETLKW ) or LOGDIE  "failed to get write lock: $fn:" . $fs->error;
  my $num = <$fh> || 0;
  return ($fh, $num);
}

sub release_lock {
  my $fn = shift;
  my $fh = shift;
  my $num = shift;
  my $justPrint = shift || 0;

  seek($fh, 0, SEEK_SET) or LOGDIE "seek failed: $fn: $!";
  print $fh "$num\n" or LOGDIE "write failed: $fn: $!";
  truncate($fh, tell($fh)) or LOGDIE "truncate failed: $fn: $!";
  my $fs = new File::FcntlLock;
  $fs->l_type(F_UNLCK);
  ALWAYS "releasing lock: $fn";
  $fs->lock( $fh, F_SETLK ) or LOGDIE "unlock failed: $fn: " . $fs->error;
  close($fh) or LOGDIE "close failed: $fn: $!";
}

Ответ 10

Одной из альтернатив подхода блокировки является использование блокирующего сокета. См. Lock::Socket в CPAN для такой реализации. Использование так же просто, как и следующее:

use Lock::Socket qw/lock_socket/;
my $lock = lock_socket(5197); # raises exception if lock already taken

Существует несколько преимуществ использования сокета:

  • гарантируется (через операционную систему), что никакие два приложения не будут иметь одну и ту же блокировку: условия гонки отсутствуют.
  • гарантируется (опять же через операционную систему) аккуратно очищаться, когда ваш процесс завершается, поэтому нет никаких устаревших блокировок.
  • полагается на функциональность, которая хорошо поддерживается во всем, что Perl работает: никаких проблем с поддержкой flock (2) на Win32, например.

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

[раскрытие: я являюсь автором вышеупомянутого модуля]

Ответ 11

Используйте flock Luke.

Изменить: Это является хорошим объяснением.

Ответ 12

flock создает блокировки файлов в стиле Unix и доступен в большинстве ОС Perl. Однако блокировки блокировки являются только рекомендательными.

edit: подчеркнул, что флока переносима

Ответ 13

Здесь мое решение для чтения и записи в одном замке...

open (TST,"+< readwrite_test.txt") or die "Cannot open file\n$!";
flock(TST, LOCK_EX);
# Read the file:
@LINES=<TST>;
# Wipe the file:
seek(TST, 0, 0); truncate(TST, 0);
# Do something with the contents here:
push @LINES,"grappig, he!\n";
$LINES[3]="Gekke henkie!\n";
# Write the file:
foreach $l (@LINES)
{
   print TST $l;
}
close(TST) or die "Cannot close file\n$!";