为什么system()返回0,即使它执行的程序死了

时间:2015-11-19 10:17:48

标签: perl system

我正在尝试测试一段代码($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?)

2 个答案:

答案 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