1

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

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。这就是我最初所做的。我的目标是能够向子进程发出信号,实际上我认为 system.js 无法做到这一点。

kill($pid,'HUP');

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

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

4

1 回答 1

2

我检查了您的代码strace在 linux 上使用该命令实际执行的操作。以下是您在其中一个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()替换当前进程。这意味着$pidfromsystem()将引用分叉的 Perl 脚本,而不是 Perl 脚本运行的命令。

于 2013-09-12T14:09:26.267 回答