使用简单文件实现锁定

时间:2010-05-12 12:46:44

标签: perl file locking

我想知道是否只有在目的地不存在的情况下才能移动文件 - 换句话说,只有在不会导致覆盖时才移动。

mv --update

似乎首先是解决方案,但是,如果源路径的时间戳比目标更新,则move将覆盖它,并且所有通过在移动之前修改时间戳来避免这种情况的尝试都将失败。

我需要这种行为来实现一个简单的基于文件的锁,其中“lock”文件的存在表明已获取锁。

我使用perl执行此任务,因此如果perl具有此功能,则会有所帮助。但是,我需要确保移动操作是原子的。

4 个答案:

答案 0 :(得分:7)

但是当别人有锁时你会怎么做?退出并稍后再试?忙等待?

如果您不需要同步,那么设置O_EXCLO_CREAT标志的sysopen是一个不错的选择,只有当文件不存在时才会创建该文件。

use Fcntl qw/ :DEFAULT /;

# ...

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

但请注意Linux open(2)手册页中的以下警告:

  只有在内核2.6或更高版本上使用NFSv3或更高版本时,才支持

O_EXCL。在未提供NFS O_EXCL支持的环境中,依赖它执行锁定任务的程序将包含竞争条件。想要使用锁文件执行原子文件锁定并且需要避免依赖O_EXCL的NFS支持的可移植程序可以在同一文件系统上创建一个唯一的文件(例如,包含主机名和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

答案 1 :(得分:0)

mv -n应该做你想做的事。

从手册页:

 -n      Do not overwrite an existing file.  (The -n option overrides any previous -f or -i options.)

答案 2 :(得分:0)

perldoc -f rename

在* NIX系统上,rename是原子的,并且满足您提出的问题/要求。但是,作为锁定文件的惯例,我经常使用@gbaconanswer中建议的O_EXCL|O_CREAT方法。

答案 3 :(得分:0)

注意如果你使用Greg解决方案,如果另一个程序试图打开它,则这两个指令之间可能存在竞争条件。

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