使用Fcntl锁定文件:涉及“使用”和“需要”的令人困惑的错误

时间:2010-07-04 17:31:55

标签: perl debugging namespaces locking

以下Perl脚本按预期输出“SUCCESS”:

use Fcntl qw(:DEFAULT :flock);
sysopen(LF, "test.txt", O_RDONLY | O_CREAT) or die "SYSOPEN FAIL: $!";
if(flock(LF, LOCK_EX)) { print "SUCCESS.\n"; }
else { print "FAIL: $!\n"; }

但现在,用

替换第一行
require "testlib.pl";

其中testlib.pl包含

use Fcntl qw(:DEFAULT :flock);

1;

现在,奇怪的是,脚本失败了,就像这样:

FAIL: Bad file descriptor

问题:为什么?

增加:

现在我知道为什么 - 谢谢! - 我想知道解决这个问题的最佳方法是什么:

  1. 只需执行use Fcntl两次,一次在主脚本中,一次在所需的库中(主脚本和库都需要它)。
  2. 将O_RDONLY替换为& O_RDONLY等
  3. 将O_RDONLY替换为O_RDONLY()等
  4. 别的什么?

3 个答案:

答案 0 :(得分:3)

通过上述use,您剥夺了Perl解析器O_RDONLY等人的知识。是无参数子程序。在这种情况下你必须更加冗长:

sysopen(LF, "test.txt", O_RDONLY() | O_CREAT()) or die "SYSOPEN FAIL: $!";
if(flock(LF, LOCK_EX())) { print "SUCCESS.\n"; }

编辑:为了进一步阐述,没有括号,O_RDONLYO_CREAT被解释为裸字(字符串),它们的行为与二进制时的预期不同。 'ed together:

$ perl -le 'print O_RDONLY | O_CREAT'
O_SVOO\Y

(个别字符按位或按字母方式。)

在这种情况下,字符串“O_SVOO \ Y”(或系统中的任何内容)被解释为数字0到sysopen,因此只要O_RDONLY,它就会工作是0(典型)并且文件已经存在(因此O_CREAT是多余的)。但fcntl显然不是对非数字参数的宽容:

$ perl -e 'flock STDOUT, "LOCK_EX" or die "Failed: $!"'
Failed: Bad file descriptor at -e line 1.

类似地:

$ perl -e 'flock STDOUT, LOCK_EX or die "Failed: $!"'
Failed: Bad file descriptor at -e line 1.

然而:

$ perl -e 'use Fcntl qw(:flock); flock STDOUT, LOCK_EX or die "Failed: $!"'
(no output)

最后,请注意use strict提供了许多有用的线索。

答案 1 :(得分:2)

use Fcntl qw(:DEFAULT :flock);不仅为您加载Fcntl库,还将一些符号导出到脚本的命名空间中。如果你将它移到另一个范围,那么常量O_RDONLY,O_CREAT,LF和LOCK_EX将不再可用,并且你的代码将不会做同样的事情 [但你仍然可以访问它们,如果你知道他们最终进入了什么命名空间 - 因为它是一个执行导出的脚本,你可以调用& main :: NAME或简单地和& NAME,但是你必须知道另一个文件正在用它的代码做什么,这不是很干净]

EXPORTED SYMBOLS下的文档中描述了这一点:

  

默认情况下,系统的F_ *和O_ *常量(例如,F_DUPFD和O_CREAT)和FD_CLOEXEC常量将导出到您的命名空间中。

     

您可以使用标签“:flock”请求提供flock()常量(LOCK_SH,LOCK_EX,LOCK_NB和LOCK_UN)。见出口商。

如果添加行

use strict;
use warnings;

到脚本的顶部,您将获得更多信息性的错误消息,例如“Name”main :: O_RDONLY“仅使用一次:可能的类型在行...”,这将为您提供这些常量定义的线索不再可见。

编辑:在回答您的问题时,最佳做法是#1,包含 每个需要它的文件中的use语句。请参阅perldoc -f use - Fcntl库只包含一次,但每次需要时都会调用import(),这就是你想要的。

答案 2 :(得分:0)

用法相当于:

BEGIN { require Module; Module->import( LIST ); }

保证在代码开始执行之前导入功能可用。当你用require替换use时,它只是在程序中的词汇点读取代码。