代码之家  ›  专栏  ›  技术社区  ›  Mansour

使用简单文件实现锁

  •  2
  • Mansour  · 技术社区  · 14 年前

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

    mv --update
    

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

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

    我将Perl用于此任务,因此如果Perl具有此功能,它将同样有用。但是,我需要确保移动操作是原子操作。

    4 回复  |  直到 11 年前
        1
  •  7
  •   Greg Bacon    14 年前

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

    如果您不需要同步,那么最好是 sysopen O_EXCL O_CREAT 设置标志,只有文件不存在时才会创建该文件。

    use Fcntl qw/ :DEFAULT /;
    
    # ...
    
    sysopen my $fh, $LOCKFILE, O_EXCL|O_CREAT
      or die "$0: sysopen: $!";
    

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

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

    __i_D宁愿有一个网络文件系统,而不是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
        2
  •  0
  •   Paul R    14 年前

    mv -n 应该做你想做的。

    从手册页:

     -n      Do not overwrite an existing file.  (The -n option overrides any previous -f or -i options.)
    
        3
  •  0
  •   Community CDub    7 年前

    perldoc -f rename

    在*nix系统上, rename 是原子的,并满足您的问题/要求。但是,作为锁文件的实践,我经常使用 O_EXCL|O_CREAT 建议的方法 @gbacon answer .

        4
  •  0
  •   dayer    11 年前

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

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