只要子进程仍在运行,就会在父进程中每隔90秒在perl中记录消息

时间:2015-03-06 15:55:23

标签: linux perl ssh

由于我公司的要求,我刚从php转到perl,所以即使这可能是一个愚蠢的问题,现在也是一种神经紧张。

我通过debian软件包在服务器上部署了一个小的perl脚本。我已经弄清楚这一切都很酷。

现在通过SSH连接从另一台服务器调用此脚本,脚本将所有操作都记录回该服务器。我使用Log :: Log4perl。

其中一项任务需要很长时间,并且还会在此过程中运行其他一些脚本。除非我记录回来,否则ssh连接的设置超时时间为5分钟。所以我想出我会创建一个子进程来运行任务,并让父进程每90秒(或其他)秒返回一次。我的问题是我不想使用睡眠因为如果任务提前完成就会弄乱日志。 我也尝试过使用Time,Time :: HiRes和alarm,但它们都会以某种方式弄乱我的日志。

这是我的代码:

    $log->info("uid $uid: calling the configure script for operation $mode,on $dst_path");

    my $pid   = fork();
    die "Could not fork\n" if not defined $pid;

    if ( $pid == 0 ) {
        configure( $script_dir, $mode, $node, $uid, $gid); # this also uses a parallel process in its execution, but we don't have a non blocking wait
    }
    while ( !waitpid( $pid, WNOHANG ) ) {
        sleep(90);
        if ( !$pid ) {
            $log->info("Still waiting for the process to finish"); # this should come up every 90 seconds of so
        }
    }

    $log->info("uid $uid: configure script executed"); # this should come up only once, now I get it every 90 seconds

    # do other stuff here after the execution of the configure sub is done

不幸的是,我继承了这种体系结构,并且无法更改它,因为有很多基于它的服务。

2 个答案:

答案 0 :(得分:1)

我试图运行代码并注意到一些可能是你的问题,但不知道配置是什么,我无法确定。这是我发现的:

  1. 调用exit
  2. 后,子进程不会configure
  3. waitpid不会更改$pid的值,因此$ pid在子项中始终为0,并且始终是父项中子项的pid。
  4. 这意味着父母永远不会写出"Still waiting for the process to finish",孩子在完成配置调用后每90秒写一次。

    此外,孩子应该永远打印90秒的消息,因为它正在等待pid 0发送CHLD信号,这不会发生,因为它没有带pid 0的孩子。

    我使用一些存根来更新你的代码,这些存根可以完成我认为你想要的(在稍微紧张的时间线上,因为我不想等待:))。我的代码做出了您可能希望更改的以下假设:

    • 每秒记录等待消息
    • 子项始终以状态值0
    • 退出

    这是我的代码:

    #!/usr/bin/env perl 
    
    use strict;
    use warnings;
    use Log::Log4perl qw(:easy);
    use POSIX qw(:sys_wait_h);
    
    Log::Log4perl->easy_init();
    
    my ($uid,$mode,$dst_path,$script_dir,$node,$gid) = (0..5);
    my $log = get_logger();
    $log->info("uid $uid: calling the configure script for operation $mode,on $dst_path");
    
    my $pid   = fork();
    die "Could not fork\n" if not defined $pid;
    
    if ( $pid == 0 ) {
        configure( $script_dir, $mode, $node, $uid, $gid); # this also uses a parallel process in its execution, but we don't have a non blocking wait
        exit(0);
    }
    my $zombie;
    while ( ($zombie = waitpid( $pid, WNOHANG ) ) != $pid) {
        $log->info("Still waiting for the process to finish"); # this should come up every 90 seconds of so
        sleep(1);
    }
    
    $log->info("uid $uid: configure script executed"); # this should come up only once, now I get it every 90 seconds
    
    # do other stuff here after the execution of the configure sub is done
    
    sub configure {
        sleep 10;
    }
    

答案 1 :(得分:1)

如果您不想睡觉,可以暂停拨打select。为了可靠地实现这一点,您可以使用self-pipe trick,其中包括创建管道,在SIGCHLD处理程序中写入管道,并使管道的读取句柄上的select调用等待。 / p>

这是一个简单的例子:

#!/usr/bin/perl
use strict;
use warnings;

use Errno qw(EINTR);
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
use Symbol qw(gensym);

sub make_non_blocking {
    my $handle = shift;
    my $flags = fcntl($handle, F_GETFL, 0)
        or die("F_GETFL: $!");
    fcntl($handle, F_SETFL, $flags | O_NONBLOCK)
        or die("F_SETFL: $!");
}

my ($read_handle, $write_handle) = (gensym, gensym);
pipe($read_handle, $write_handle)
    or die("pipe: $!");

make_non_blocking($read_handle);
make_non_blocking($write_handle);

local $SIG{CHLD} = sub {
    syswrite($write_handle, "\0", 1);
};

my $pid = fork();
die("fork: $!") if !defined($pid);

if ($pid == 0) {
    sleep(10);
    exit;
}

my $rin = '';
vec($rin, fileno($read_handle), 1) = 1;

while (1) {
    my $nfound = select(my $rout = $rin, undef, undef, 2);
    if ($nfound < 0) {
        # Error. Must restart the select call on EINTR.
        die("select: $!") if $! != EINTR;
    }
    elsif ($nfound == 0) {
        # Timeout.
        print("still running...\n");
    }
    else {
        # Child exited and pipe was written to.
        last;
    }
}

waitpid($pid, 0);

close($read_handle);
close($write_handle);