【问题标题】:Waiting on a child process in perl在 perl 中等待子进程
【发布时间】:2013-09-15 17:51:54
【问题描述】:

我在捕获子进程的返回状态时遇到问题。下面是我的代码的简化版本。

use Modern::Perl;
use POSIX;
use AnyEvent;

my @jobs = (1, 7, 3, 9 , 4 , 2);
my %pid;
my %running;

my $t = AE::timer 0, 5, sub{
    while(scalar( keys %running < 3) && scalar (@jobs)){
        my $job = shift @jobs;
        $running{$job}=1;
        $pid{$job} = run($job);
    }
    for(keys %running){
        delete $running{$_} unless check($pid{$_},$_);
    }
    exit unless scalar keys %running;
};

AnyEvent->condvar->recv;

sub func_to_run{
    my $id = shift;
    close STDOUT;
    open STDOUT, ">>$id.log";
    exec '/bin/sleep', $id;
}

sub run{
    my $id = shift;
    print "starting job $id\n";
    my $pid = fork();
    return $pid if $pid;
    func_to_run($id);
}

sub check{
    my ($pid,$id) = @_;
    my $result = waitpid($pid, WNOHANG);
    {
        if ($result == $pid) {
            my $rc = $? >> 8;
            print "Job $id finished with code $rc\n";
            return 0;
        }
        elsif ($result == -1 and $! == ECHILD) {
            print "Job $id finished running, not sure if it was sucessfull\n";
            return 0;
        }
        elsif ($result == 0) {
            return 1;
        }
        redo;
    }
}

输出:

starting job 1
starting job 7
starting job 3
Job 1 finished running, not sure if it was sucessfull
Job 3 finished running, not sure if it was sucessfull
starting job 9
starting job 4
Job 7 finished running, not sure if it was sucessfull
starting job 2
Job 4 finished running, not sure if it was sucessfull
Job 9 finished running, not sure if it was sucessfull
Job 2 finished running, not sure if it was sucessfull

为什么 waitpid() 返回 -1 而不是返回状态?

编辑: 我将 system + exit 更改为 exec。这就是我最初所做的。我的目标是能够向子进程发出信号,我实际上认为系统无法做到这一点。

kill($pid,'HUP');

编辑 2: 可以同时运行多个子进程,这是从 AE::timer 模块调用的。我想在这里弄清楚为什么我从 waitpid() 得到-1,这表明孩子被收割了。

编辑 3:我已将代码更改为完整的工作示例,并得到输出

【问题讨论】:

  • 您是否在某处设置了$SIG{CHLD} = 'IGNORE';
  • 不,我没有设置那个信号处理程序。我已将问题编辑得更清楚
  • 等等,什么?您希望子进程向自己发出信号吗? kill HUP =&gt; $$ 但是……为什么?
  • 不,我希望父进程能够向子进程发出信号。
  • 你为什么使用WNOHANG?请注意,您的代码甚至没有声明该符号(或ECHILD)!始终使用use strict; use warnings;

标签: perl unix posix fork ipc


【解决方案1】:

我检查了您的代码在 linux 上使用 strace 命令实际执行的操作。以下是您在 sleep 命令之一完成时看到的内容:

$ strace -f perl test.pl
...
[pid 4891] nanosleep({1, 0}, NULL) = 0
[pid 4891] 关闭(1)= 0
[pid 4891] 关闭(2) = 0
[pid 4891] exit_group(0) = ?
[pid 4891] +++ 以 0 +++ 退出
 2061530, 64, 4990) = -1 EINTR(中断的系统调用)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4891, si_status=0, si_utime=0, si_stime=0} ---
写(4,“\1\0\0\0\0\0\0\0”,8)= 8
rt_sigreturn() = -1 EINTR(中断的系统调用)
clock_gettime(CLOCK_MONOTONIC, {97657, 317300660}) = 0
clock_gettime(CLOCK_MONOTONIC, {97657, 317371410}) = 0
epoll_wait(3, {{EPOLLIN, {u32=4, u64=4294967300}}}, 64, 3987) = 1
clock_gettime(CLOCK_MONOTONIC, {97657, 317493076}) = 0
读(4, "\1\0\0\0\0\0\0\0", 8) = 8
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, NULL) = 4891
wait4(-1, 0x7fff8f7bc42c, WNOHANG|WSTOPPED|WCONTINUED, NULL) = -1 ECHILD(无子进程)
clock_gettime(CLOCK_MONOTONIC, {97657, 317738921}) = 0
epoll_wait(3, {}, 64, 3986) = 0
clock_gettime(CLOCK_MONOTONIC, {97661, 304667812}) = 0
clock_gettime(CLOCK_MONOTONIC, {97661, 304719985}) = 0
epoll_wait(3, {}, 64, 1) = 0
...

[pid 4891] 开头的行来自sleep 命令,其余的来自您的脚本。您可以看到脚本正在调用wait4() 系统调用并返回睡眠进程的 PID — 大概是脚本正在使用的事件循环的一部分。这就是为什么你在调用 waitpid() 时得到 -1 的原因——子进程已经被收割了。

顺便说一句,AnyEvent 文档中有一节 (CHILD PROCESS WATCHERS) 是关于监视子进程并检查它们的返回码的。来自文档:

my $done = AnyEvent->condvar;

my $pid = fork or exit 5;

my $w = AnyEvent->child (
   pid => $pid,
   cb  => sub {
      my ($pid, $status) = @_;
       warn "pid $pid exited with status $status";
      $done->send;
   },
);

# do something else, then wait for process exit
$done->recv;

关于使用system()exec() 生成进程,使用exec() 是正确的。这是因为system() 创建了一个子进程来执行其命令,而exec() 用命令替换当前进程。这意味着来自system()$pid 将引用派生的Perl 脚本,而不是Perl 脚本运行的命令。

【讨论】:

    猜你喜欢
    • 2011-07-05
    • 1970-01-01
    • 1970-01-01
    • 2011-05-24
    • 2011-03-25
    • 2014-04-01
    • 1970-01-01
    相关资源
    最近更新 更多