如何在Perl中锁定文件?

时间:2008-08-29 18:13:21

标签: perl file locking

在Perl中创建文件锁的最佳方法是什么?

最好是聚集文件或创建一个锁定文件来锁定并检查锁定文件上的锁定吗?

14 个答案:

答案 0 :(得分:28)

如果你最终使用flock,这里有一些代码:

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' - $!";

一些有用的链接:

为了回答您添加的问题,我要么说要锁定文件,要么在文件被锁定时创建一个你称为“锁定”的文件,并在文件被锁定时将其删除(然后确保你的程序服从那些语义。)

答案 1 :(得分:11)

其他答案涵盖了Perl flock很好的锁定,但在许多Unix / Linux系统上实际上有两个独立的锁定系统:BSD flock()和基于POSIX fcntl()的锁。

除非在构建Perl时提供配置的特殊选项,否则它的flock将使用flock()(如果可用)。这通常很好,如果您只需要在应用程序中锁定(在单个系统上运行),可能就是您想要的。但是,有时您需要与另一个使用fcntl()锁的应用程序(如许多系统上的Sendmail)进行交互,或者您需要跨NFS安装的文件系统进行文件锁定。

在这些情况下,您可能需要查看File::FcntlLockFile::lockf。也可以在纯Perl中执行基于fcntl()的锁定(带有一些有毛的和非便携式的pack())。

flock / fcntl / lockf差异的快速概述:

lockf几乎总是在fcntl之上实现,只有文件级锁定。如果使用fcntl实现,则以下限制也适用于lockf。

fcntl提供范围级锁定(在文件中)和NFS上的网络锁定,但fork()之后子进程不会继承锁。在许多系统上,您必须将文件句柄打开为只读以请求共享锁,并使用读写来请求独占锁。

flock只有文件级锁定,锁定只在一台机器内(你可以锁定一个挂载NFS的文件,但只有本地进程会看到锁定)。锁由子进程继承(假设文件描述符未关闭)。

有时(SYSV系统)flock是使用lockf或fcntl模拟的;在一些BSD系统上,使用flock模拟lockf。通常这些仿真工作效果很差,建议你避免使用它们。

答案 2 :(得分:7)

CPAN救援:IO::LockedFile

答案 3 :(得分:6)

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?)和魔法双参数open(它是Perl代码中许多(n可利用的)错误的来源),并始终测试您的{{ 1}}成功。

答案 4 :(得分: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 $!";

在PerlMonks上查看完整的documentation of flockFile locking tutorial,即使它也使用了旧式的文件句柄用法。

实际上我通常会跳过close()上的错误处理,因为没有 如果它无论如何都会失败我能做多少。

关于要锁定的内容,如果您在单个文件中工作,则锁定该文件。如果您需要同时锁定多个文件,那么 - 为了避免死锁 - 最好选择一个锁定的文件。如果这是您真正需要锁定的几个文件之一或者您为锁定目的而创建的单独文件,这并不重要。

答案 5 :(得分:4)

您是否考虑过使用LockFile::Simple module?它已经完成了大部分工作。

根据我过去的经验,我发现它很容易使用和坚固。

答案 6 :(得分:3)

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' - $!";

答案 7 :(得分:1)

我在这个问题中的目标是锁定一个文件,用作多个脚本的数据存储。最后我使用了类似的代码(来自Chris):

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

在他的例子中,我移除了群FILE,8,因为关闭(FILE)也执行此操作。真正的问题是当脚本启动时它必须保持当前计数器,当它结束时它必须更新计数器。这是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);

答案 8 :(得分:1)

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: $!";
}

答案 9 :(得分:1)

文件方法的一种替代方法是使用锁套接字。有关此类实施,请参阅CPAN上的Lock::Socket。用法非常简单:

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

使用套接字有几个好处:

  • 保证(通过操作系统)没有两个应用程序将保持相同的锁定:没有竞争条件。
  • 保证(再次通过操作系统)在进程退出时整齐地清理,因此没有陈旧的锁来处理。
  • 依赖于Perl运行的任何东西都支持的功能:例如,在Win32上没有flock(2)支持的问题。

明显的缺点当然是锁名称空间是全局的。如果另一个进程决定锁定您需要的端口,则可能存在一种拒绝服务。

[披露:我是提到的模块的作者]

答案 10 :(得分:0)

使用flock卢克。

修改: This是一个很好的解释。

答案 11 :(得分:0)

flock创建了Unix风格的文件锁,并且可以在大多数操作系统的Perl上运行。然而,flock的锁只是建议性的。

编辑:强调鸡群是便携式的

答案 12 :(得分:0)

这是我在一个锁中读写的解决方案......

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$!";

答案 13 :(得分:0)

群可能是最好的,但是它要求您编写所有支持的代码-超时,过时的锁定,不存在的文件等。 我对LockFile :: Simple进行了修正,但是发现它开始将默认umask设置为只读,并且没有清除它。在modperl上的多进程/多线程应用程序上导致随机权限问题 我已经决定使用一些空文件处理来包装NFSLock。