【问题标题】:Why system() returns 0 even though the program it executes dies为什么 system() 即使它执行的程序死了也返回 0
【发布时间】:2016-02-21 10:11:58
【问题描述】:

我正在尝试测试一段代码 ($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),因为它无法获得锁和dies。但是我得到:

$ perl test_lock
First system(): 0
Another instance of /tmp/x is already running. Exiting ...
Second system(): 0

我的假设有什么问题? (有没有更好的方法来测试$code?)

【问题讨论】:

  • 第二个系统调用(在 cygwin 上)的退出代码为 2816,但在 linux 机器上我得到 0... 奇怪。
  • 我在 linux 机器上也得到了代码 2816,我无法重现退出代码 0。
  • 好的,我尝试运行它几次 (for n in 1 2 3 4 5; do echo "--&gt; run n. $n"; perl test_lock; sleep 5; done),我得到了预期的行为 5 次中有 2 次。我运行 Linux。
  • 我怀疑这与您在第一次调用时背景 &amp; 而不是第二次调用有关,但我无法确认。
  • 因此,想要锁定一个文件(多次)然后什么都不做,这似乎有点奇怪......你想要的实际用例是什么?

标签: perl system


【解决方案1】:

我认为这可能是因为您有竞争条件。你怎么知道错误实际上来自你的第二个过程?

因为如果你例如运行:

perl /tmp/x & perl /tmp/x ; echo $?

您可能会得到零回报,因为比赛的“获胜者”很可能是后一个过程(您正在捕捉的返回代码)。 (多试几次,你会看到不同的结果)

shell 在这两个命令之间所做的工作也确实略有不同 - 来自文档:

如果只有一个标量参数,则检查参数是否有 shell 元字符,如果有,则将整个参数传递给系统的命令 shell 进行解析(在 Unix 平台上是 /bin/sh -c,但在其他平台上有所不同)。如果参数中没有shell元字符,则将其拆分为单词并直接传递给 execvp ,这样效率更高。

所以实际上您应该在第一次看到perl 之前调用sh,这意味着实际上更有可能需要更长的时间才能到达锁定点。

这意味着你的命令更像:

sh -c "perl /tmp/x"& perl /tmp/x; echo $?

运行几次,看看有多少次得到非零错误代码。这并不常见,因为通常 shell 启动的“延迟”足以确保第二个实例在大多数情况下赢得比赛!

如果你有 linux - 试试strace -fTt yourscript,它将跟踪执行流程。或者您可以明智地使用$$ 在运行时报告进程pid。

【讨论】:

    【解决方案2】:

    在这两种情况下,您都将获得您启动的 shell 的退出代码。粗略地说,shell 返回它运行的最后一个程序的退出代码。

    由于system("perl $progfile &amp;") 创建的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
    

    【讨论】:

    • "在这两种情况下,您都将获得您启动的 shell 的退出代码。" - 我不认为第二次调用 system() 涉及 shell,因为它的参数字符串中没有 shell 元字符。
    • 确实如此。为了简单起见,我将保留答案,但优化确实发挥了作用
    猜你喜欢
    • 2019-04-03
    • 1970-01-01
    • 2012-07-14
    • 1970-01-01
    • 1970-01-01
    • 2018-04-04
    • 2012-06-21
    • 1970-01-01
    相关资源
    最近更新 更多