由于我公司的要求,我刚从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
不幸的是,我继承了这种体系结构,并且无法更改它,因为有很多基于它的服务。
答案 0 :(得分:1)
我试图运行代码并注意到一些可能是你的问题,但不知道配置是什么,我无法确定。这是我发现的:
exit
configure
waitpid
不会更改$pid
的值,因此$ pid在子项中始终为0,并且始终是父项中子项的pid。这意味着父母永远不会写出"Still waiting for the process to finish"
,孩子在完成配置调用后每90秒写一次。
此外,孩子应该永远打印90秒的消息,因为它正在等待pid 0发送CHLD信号,这不会发生,因为它没有带pid 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);