为什么waitpid不会等待进程退出?

9
在下面的脚本中,我正在尝试弄清楚waitpid是如何工作的,但它不等待ssh进程退出。 done立即打印,而不是在ssh进程存在后打印。
问题:如何使waitpid仅在我给出的pid已经退出时才继续执行?
#!/usr/bin/perl
use strict;
use warnings;
use Parallel::ForkManager;
use POSIX ":sys_wait_h";

my $pm = Parallel::ForkManager->new(5);
my $pid = $pm->start;
my $p = $pid;
if (!$pid) {
    system("ssh 10.10.47.47 sleep 10");
    $pm->finish;
}

$p = qx(/usr/bin/pgrep -P $p);
print "ssh pid is $p\n";

my $kid;
do {
    $kid = waitpid($p, 0);
} while $kid > 0;

print "done\n";

我也尝试过

while (1) {
    $p = kill 0, $p;
    print "x";
    sleep 1 if $p;
    print "*";
    last unless $p;
}

但由于某种原因它甚至没有到达第一个print语句,而且从未退出。

1个回答

9
wait 函数家族只能在子进程上工作,即使是 waitpid。睡眠进程不是你的子进程,它是你的孙子进程。这是因为 system 本质上是 forkexec 的组合。通过使用 Parallel::ForkManager + system,你会先进行一次 fork,然后再进行一次 fork,最后执行 sleep。
既然你已经进行了 fork,那么应该使用 exec。这种方法的额外优势是不需要调用 pgrep 并解决其时间问题(例如,在子进程执行 system 之前父进程可能会调用 pgrep)。
my $pm = Parallel::ForkManager->new(5);
my $pid = $pm->start;
my $p = $pid;
if (!$pid) {
    no warnings;  # no warnings "exec" is not working
    exec("sleep 10");
    $pm->finish;
}

print "sleep pid is $p\n";
waitpid($p, 0);

为了简单起见,现在使用sleep。Perl会发出警告:“不太可能到达的语句”,因为Perl没有意识到$pm->start已经分叉。这应该是no warnings "exec",但它不起作用,所以我不得不抑制所有警告。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接