Perl 使用简单文件实现锁

Perl 使用简单文件实现锁,perl,file,locking,Perl,File,Locking,我想知道是否有任何方法可以仅在目标不存在的情况下移动文件-换句话说,仅在不会导致覆盖的情况下移动文件 mv --update 似乎首先是解决方案,但是,如果源路径的时间戳比目标路径的时间戳新,则move将覆盖它,并且所有通过在移动失败之前修改时间戳来规避此问题的尝试都将失败 我需要这种行为来实现一个简单的基于文件的锁,其中“锁”文件的存在表示锁已被获取 我使用perl来完成这个任务,所以如果perl有这个功能,它也会很有帮助。但是,我需要确保移动操作是原子的。mv-n应该执行您想要的操作 从手

我想知道是否有任何方法可以仅在目标不存在的情况下移动文件-换句话说,仅在不会导致覆盖的情况下移动文件

mv --update
似乎首先是解决方案,但是,如果源路径的时间戳比目标路径的时间戳新,则move将覆盖它,并且所有通过在移动失败之前修改时间戳来规避此问题的尝试都将失败

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


我使用perl来完成这个任务,所以如果perl有这个功能,它也会很有帮助。但是,我需要确保移动操作是原子的。

mv-n
应该执行您想要的操作

从手册页:

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

但是当别人有锁的时候你会怎么做?退出,稍后再试?忙着等吗

如果不需要同步,那么最好使用
O_EXCL
O_create
标志集,这将仅在文件不存在时创建文件

use Fcntl qw/ :DEFAULT /;

# ...

sysopen my $fh, $LOCKFILE, O_EXCL|O_CREAT
  or die "$0: sysopen: $!";
但是请注意Linux
open(2)
manual页面中的以下警告:

只有在内核2.6或更高版本上使用NFSv3或更高版本时,NFS才支持
O_EXCL
。在未提供NFS
O_exc
支持的环境中,依赖它执行锁定任务的程序将包含争用条件。要使用锁文件执行原子文件锁定,并且需要避免依赖NFS对
O_exc
的支持的便携式程序可以在同一文件系统上创建唯一的文件(例如,合并主机名和PID),并使用
link(2)
创建指向锁文件的链接。如果
链接(2)
返回0,则锁定成功。否则,对唯一文件使用
stat(2)
检查其链接计数是否已增加到2,在这种情况下,锁定也成功

俗话说:“我宁愿有一个网络文件系统而不是NFS”,所以如果可以的话,请将协调进程保持在同一台机器上

你可以考虑在下面的代码中使用:

#! /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;
my$KIDS=5;
我的%pids;
适用于(1..$KIDS){
我的$pid=叉子;
模具“$0:fork:$!”除非定义为$pid;
$pid?+$pid{$pid}:工作;
}
while(my$pid=wait){
如果$pid==-1,则为最后一个;
警告“$0:未知子项$pid”,除非删除$pid{$pid};
}
警告“$0:仍处于活动状态:”。
join(“,”=>sort{$a$b}键%pids)。
“\n”
如果键%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 /儿童锁:26644有锁 /儿童锁:26645有锁 /儿童锁:26646有锁 /儿童锁:26645有锁 /儿童锁:26648有锁 /儿童锁:26646有锁 /儿童锁:26647有锁 /儿童锁:26647有锁 /儿童锁:26644有锁 /kidlock:26648有锁
perldoc-f


On*NIX系统是原子的,满足您提出的问题/需求。但是,作为锁文件的一种实践,我经常使用s中建议的
O|u EXCL | O|u CREAT
方法。

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

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

请注意,并非所有版本的
mv
都有此选项。Mac OS X(基于BSD)似乎有,但RedHat上的GNU版本没有。也许你的RedHat coreutils版本已经过时了?我现在不在GNU/Linux机器旁,但它在文档中@大卫:谢谢你指出这一点-似乎
-n
并不适用于所有地方-一些Linux(和cygwin)用户可能需要一个替代解决方案,尽管没有明显的想法。@cikkle运行RHEL5.4,其中包含coreutils 5.97。据git称,添加“-n”的更改是在2009年1月提交的,对于redhat来说可能太新了,无法将其移动到“稳定”版本中。ye-我的prod环境中没有-n。非常感谢您的详细回复。如果工作代码在退出之前释放锁,这将是合适的。但是在我的例子中,获取锁的进程退出,然后运行以释放它。在这两者之间,其他进程可能需要获取锁,在这种情况下,它们只是失败(无忙等待)。可以将此视为一种类似http的无状态设置。@mikeY在这种情况下,我建议将
sysopen
O|u EXCL | O|u create
一起使用,因为如果锁文件存在,它将失败,只有在没有其他人创建它时才会成功。