我正在尝试测试一段代码($code
),以确保一次只运行一个程序实例:
#!/usr/bin/perl
# test_lock
use strict;
use warnings;
( my $code = <<'CODE') =~ s/^\s+//gm;
#!/usr/bin/perl
use strict;
use warnings;
use Fcntl qw(:flock);
# Make sure only one instance of the program is running at a time.
open our $Lock, '<', $0 or die "Can't lock myself $0: $!";
flock $Lock, LOCK_EX | LOCK_NB
or die "Another instance of $0 is already running. Exiting ...\n";
sleep(2);
CODE
my $progfile = '/tmp/x';
open my $fh, '>', $progfile or die $!;
print $fh $code;
close $fh;
$|++;
my $ex1 = system("perl $progfile &");
print "First system(): $ex1\n";
my $ex2 = system("perl $progfile");
print "Second system(): $ex2\n";
我预计对system()
的第二次调用会返回一个非零值($ex2
),因为它无法获得锁定和die
。但是我明白了:
$ perl test_lock
First system(): 0
Another instance of /tmp/x is already running. Exiting ...
Second system(): 0
我的假设有什么问题? (有没有更好的方法来测试$code
?)
答案 0 :(得分:5)
我认为可能是因为你有竞争条件。你怎么知道这个错误实际上来自你的第二个过程?
因为例如,你可以运行:
perl /tmp/x & perl /tmp/x ; echo $?
您可能获得零回报,因为“赢家”#39;比赛可能是后一个过程(返回你正在捕捉的代码)。 (尝试几次,你会看到不同的结果)
你也确实在两个命令之间做了些什么 - 来自文档:
如果只有一个标量参数,则检查参数是否为shell元字符,如果有,则将整个参数传递给系统的命令shell进行解析(这是/ bin / sh -c在Unix平台上,但在其他平台上有所不同)。如果参数中没有shell元字符,它将被拆分为单词并直接传递给execvp,这样效率更高。
所以实际上你应该在第一次sh
之前看到perl
的调用,这意味着它实际上更有可能需要更长时间才能到达锁定点。
这意味着你的命令更像是:
sh -c "perl /tmp/x"& perl /tmp/x; echo $?
运行几次,看看你得到非零错误代码的次数。它并不常见,因为通常是“延迟”。 shell启动足以确保第二个实例在大多数时间赢得比赛!
如果你是linux - 尝试strace -fTt yourscript
,它将跟踪执行流程。或者,您可以在运行时明智地使用$$
报告进程pid。
答案 1 :(得分:4)
在这两种情况下,您都将获得启动的shell的退出代码。粗略地说,shell返回它运行的最后一个程序的退出代码。
由于system("perl $progfile &")
创建的shell不会等待子项结束,因此它几乎总是返回0
,因为在后台启动perl
不太可能导致错误。
因此,如果perl
的第二个实例首先获得锁定,您将获得结果。如果您确定异常的来源,则可以更清楚地看到这种竞争条件。
#!/usr/bin/perl
# test_lock
use strict;
use warnings;
( my $code = <<'CODE') =~ s/^\s+//gm;
#!/usr/bin/perl
use strict;
use warnings;
use Fcntl qw(:flock);
# Make sure only one instance of the program is running at a time.
open our $Lock, '<', $0 or die "Can't lock myself $0: $!";
flock $Lock, LOCK_EX | LOCK_NB
or die "$ARGV[0]: Another instance of $0 is already running. Exiting ...\n";
sleep(2);
CODE
my $progfile = 'b.pl';
open my $fh, '>', $progfile or die $!;
print $fh $code;
close $fh;
$|++;
my $ex1 = system("perl $progfile 1 &");
print "First system(): $ex1\n";
my $ex2 = system("perl $progfile 2");
print "Second system(): $ex2\n";
输出:
$ perl a.pl
First system(): 0
1: Another instance of b.pl is already running. Exiting ...
Second system(): 0
$ perl a.pl
First system(): 0
2: Another instance of b.pl is already running. Exiting ...
Second system(): 2816