【问题标题】:coreutils timeout simulate on perl在 perl 上模拟 coreutils 超时
【发布时间】:2013-02-23 15:11:48
【问题描述】:

我尝试使用 perl 替换超时(centos5 中需要)

这里是脚本:

#!/usr/bin/perl
use strict;
my $pid=$$;
my $timeout=shift;
my @args=@ARGV;
eval {
        local $SIG{ALRM} = sub {
          print "Timed OUT!\n";
          exit 142;
          kill 9,$pid;
        };
        alarm($timeout);
        system(@args);
};
exit $?;

在测试时我发现:

一切正常

time /tmp/timeout 3 sleep 6
Timed OUT!

real    0m3.007s
user    0m0.000s
sys     0m0.004s

但这里都不好

time echo `/tmp/timeout 3 sleep 6`
Timed OUT!

real    0m6.009s
user    0m0.000s
sys     0m0.004s

在我使用 /usr/bin/timeout 测试的 debian 系统上:

time echo `/usr/bin/timeout 3 sleep 6`


real    0m3.004s
user    0m0.000s
sys     0m0.000s

那么问题

  • 为什么 perl 脚本的工作如此奇怪?
  • 是否有任何真正的工作方式可以在 perl 上写入超时,其工作方式与二进制超时相同?

请注意,我知道 /usr/share/doc/bash-3.2/scripts/timeout,而且我还发现它的作用与我的 perl 方法相同

另外请注意,我无法在针对此脚本的服务器上安装来自 CPAN 的模块

我尝试使用exec(),但在这种情况下,它无法处理 sub 中的信号。

UPD

使用来自@rhj 的脚本(必须稍作修正)

#!/usr/bin/perl
use strict;
use warnings;
my $PID=$$;
my $timeout=shift;
my @args=@ARGV;

my $pid = fork();
defined $pid or die "fork: $!";
$pid == 0 && exec(@args);

my $timed_out = 0;
$SIG{ALRM} = sub { $timed_out = 1; die; };
alarm $timeout;
eval { waitpid $pid, 0 };
alarm 0;
if ($timed_out) {
    print "Timed out!\n";
    kill 9, $pid;
    kill 9, $PID;
}
elsif ($@) {
    warn "error: $@\n";
}

上面测试通过,调用外部脚本失败:

run_script

#!/bin/sh
sleep 6

test.sh

#!/bin/sh
a=`./timeout.pl 2 ./run_script.sh`

输出

$ time ./test.sh 

real    0m6.020s
user    0m0.004s
sys     0m0.008s

【问题讨论】:

    标签: perl bash timeout


    【解决方案1】:

    此版本应始终有效:

    #!/usr/bin/perl
    use strict;
    use warnings;
    my $pid=$$;
    my $timeout=shift;
    my @args=@ARGV;
    
    my $pid = fork();
    defined $pid or die "fork: $!";
    $pid == 0 && exec(@args);
    
    my $timed_out = 0;
    $SIG{ALRM} = sub { $timed_out = 1; die; };
    alarm $timeout;
    eval { waitpid $pid, 0 };
    alarm 0;
    if ($timed_out) {
        print "Timed out!\n";
        kill 9, $pid;
    }
    elsif ($@) {
        warn "error: $@\n";
    }
    

    不过,它不会处理 exec() 调用中的错误。

    【讨论】:

    • 和您的未修复示例:"my" variable $pid masks earlier declaration in same scope at /tmp/timeout line 8.
    • 有测试用例还是不行的时候:gist.github.com/zba/5021950
    【解决方案2】:

    必须使用 IPC::Cmd 来实现;

    #!/usr/bin/perl -w 
    use strict;
    use IPC::Cmd qw(run_forked);
    my $timeout=shift;
    my $stdout;
    my $hashref = run_forked(@ARGV, { timeout => $timeout});
    print $hashref->{'stdout'};
    print STDERR $hashref->{'stderr'}; 
    if ($hashref->{'timeout'}) {            
            print STDERR "Timed out!\n";
            exit 142;
    }
    exit $hashref->{'exit_code'};
    

    我不得不使用 rpmforge 安装 IPC::Cmd。

    【讨论】:

    • 我认为您不能在此脚本的目标服务器上安装来自 CPAN 的模块IPC::CmdCPAN 上。
    猜你喜欢
    • 2015-03-05
    • 1970-01-01
    • 2013-01-08
    • 2012-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-27
    相关资源
    最近更新 更多