在Perl中等待子进程 [英] Waiting on a child process in perl

查看:118
本文介绍了在Perl中等待子进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am having an issue with capturing the return status of the child process. below is a simplified version of my code.

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而不是返回状态?

why is waitpid() returning -1 instead of a return status?

我将系统+退出更改为exec.这就是我最初的工作.我的目标是能够发信号通知子进程,而我实际上认为这无法通过系统来完成.

I changed system + exit to exec. This was what I was originally doing. My goal is to be able to signal the child process, which I don't actually think can be done with system.

kill($pid,'HUP');

一次可以运行多个子进程,这是从AE :: timer模块调用的.我要在这里弄清楚的是为什么我从waitpid()中得到-1表示孩子被收割了.

EDIT 2: There can be several child processes running at once, and this is being called from a AE::timer module. what I want to figure out here is why I am getting -1 from waitpid() which indicates that the child was reaped.

我已将代码更改为具有我得到的输出的完整工作示例

EDIT 3: I have changed the code to a full working example with the output I get

推荐答案

我在Linux上使用strace命令检查了您的代码实际上在做什么.以下是sleep命令之一完成时看到的内容:

I checked what your code is actually doing with the strace command on linux. The following is what you see as one of the sleep commands completes:

$ strace -f perl test.pl
...
[pid  4891] nanosleep({1, 0}, NULL)     = 0
[pid  4891] close(1)                    = 0
[pid  4891] close(2)                    = 0
[pid  4891] exit_group(0)               = ?
[pid  4891] +++ exited with 0 +++
 2061530, 64, 4990) = -1 EINTR (Interrupted system call)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4891, si_status=0, si_utime=0, si_stime=0} ---
write(4, "\1\0\0\0\0\0\0\0", 8)         = 8
rt_sigreturn()                          = -1 EINTR (Interrupted system call)
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
read(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 (No child processes)
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的原因-子进程已经被获取.

The lines starting [pid 4891] are from the sleep command, and the rest are from your script. You can see that the script is invoking the wait4() system call and returning the PID of the sleep process — presumably as part of the event loop that the script is using. This is why you’re getting -1 from your call to waitpid() — the child process has already been reaped.

顺便说一下,AnyEvent文档有一个部分(儿童过程观察者)观察子过程并检查其返回码.从文档中:

By the way, the AnyEvent documentation has a section (CHILD PROCESS WATCHERS) on watching child processes and examining their return codes. From the documentation:

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脚本运行的命令.

With regard to using system() or exec() to spawn the process, you are correct to use exec(). This is because system() creates a sub-process to execute its command in whereas exec() replaces the current process with the command. This means that the $pid from the system() would refer to the forked Perl script, and not to the command run by the Perl script.

这篇关于在Perl中等待子进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆