我遇到了捕获子进程返回状态的问题。下面是我的代码的简化版本。
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:我已经使用输出
将代码更改为完整的工作示例答案 0 :(得分:2)
我检查了你的代码在linux上用strace
命令实际做了些什么。以下是您看到的sleep
命令之一:
$ 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的原因 - 已经收到了子进程。
顺便说一句,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()
用命令替换当前进程。这意味着$pid
中的system()
将引用分叉的Perl脚本,而不是Perl脚本运行的命令。