Реализация блокировки с использованием простых файлов - PullRequest
2 голосов
/ 12 мая 2010

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

mv --update

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

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

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

Ответы [ 4 ]

7 голосов
/ 12 мая 2010

Но что вы будете делать, пока у кого-то еще есть замок? Выйти и попробовать позже? Busy-ждать?

Если вам не нужна синхронизация, хорошая ставка - sysopen с установленными флагами O_EXCL и O_CREAT, что создаст файл, только если он не существует.

use Fcntl qw/ :DEFAULT /;

# ...

sysopen my $fh, $LOCKFILE, O_EXCL|O_CREAT
  or die "$0: sysopen: $!";

Но обратите внимание на следующее предостережение со страницы руководства Linux open(2):

O_EXCL поддерживается только в NFS при использовании NFSv3 или новее в ядре 2.6 или новее. В средах, где поддержка NFS O_EXCL не предоставляется, программы, которые используют ее для выполнения задач блокировки, будут содержать условие состязания. Переносимые программы, которые хотят выполнять атомарную блокировку файлов с помощью файла блокировки и должны избегать использования поддержки NFS для O_EXCL, могут создавать уникальный файл в той же файловой системе (, например, , включая имя хоста и PID) и используйте link(2), чтобы сделать ссылку на файл блокировки. Если link(2) возвращает 0, блокировка успешна. В противном случае используйте stat(2) для уникального файла, чтобы проверить, увеличилось ли количество ссылок до 2, и в этом случае блокировка также будет успешной.

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

Вы можете использовать flock, как показано в коде ниже:

#! /usr/bin/perl

use warnings;
use strict;

use Fcntl qw/ :DEFAULT :flock /;

my $LOCKFILE = "/tmp/mylock";

sub acquire_lock {
  sysopen my $fh, $LOCKFILE, O_RDWR|O_CREAT or die "$0: open: $!";
  flock $fh, LOCK_EX                        or die "$0: flock: $!";
  $fh;
}

sub work {
  for (1 .. 2) {
    my $fh = acquire_lock;
    print "$0: $$ has lock\n";
    sleep rand 3;
    close $fh or warn "$0: [$$] close: $!";
  }
  exit;
}

Для демонстрации приведенный ниже код разветвляет пятерых детей, которые по очереди приобретают замок:

my $KIDS = 5;
my %pids;
for (1 .. $KIDS) {
  my $pid = fork;
  die "$0: fork: $!" unless defined $pid;

  $pid ? ++$pids{$pid} : work;
}

while (my $pid = wait) {
  last if $pid == -1;
  warn "$0: unknown child $pid" unless delete $pids{$pid};
}

warn "$0: still alive: " .
     join(", " => sort { $a <=> $b } keys %pids) .
     "\n"
  if keys %pids;

Пример вывода:

./kidlock: 26644 has lock
./kidlock: 26645 has lock
./kidlock: 26646 has lock
./kidlock: 26645 has lock
./kidlock: 26648 has lock
./kidlock: 26646 has lock
./kidlock: 26647 has lock
./kidlock: 26647 has lock
./kidlock: 26644 has lock
./kidlock: 26648 has lock
0 голосов
/ 11 февраля 2014

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

sysopen my $fh, $LOCKFILE, O_RDWR|O_CREAT or die "$0: open: $!";
flock $fh, LOCK_EX                        or die "$0: flock: $!";
0 голосов
/ 14 мая 2010

perldoc -frename

В * NIX системах rename является атомарным и соответствует буквально вашему вопросу / требованиям. Однако на практике для файлов блокировок я часто использую подход O_EXCL|O_CREAT, предложенный в @ gbacon answer .

0 голосов
/ 12 мая 2010

mv -n должен делать то, что вы хотите.

со страницы руководства:

 -n      Do not overwrite an existing file.  (The -n option overrides any previous -f or -i options.)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...