perl:一个父母,许多孩子 - 父母中有一个管道阅读器?

Posted

技术标签:

【中文标题】perl:一个父母,许多孩子 - 父母中有一个管道阅读器?【英文标题】:perl: one parent, many children - single pipe reader in parent? 【发布时间】:2018-10-09 14:31:12 【问题描述】:

是否有可能在 perl 中以这样一种方式建立一个管道,即父级只有一个 READER 管道,而许多子级在它们开始/退出时写入它?

典型的食谱代码是:

#!/usr/bin/perl -w
# pipe2 - use pipe and fork so child can send to parent

use IO::Handle;
pipe(READER, WRITER);
WRITER->autoflush(1);

if ($pid = fork) 
    close WRITER;
    chomp($line = <READER>);
    print "Parent Pid $$ just read this: `$line'\n";
   // do what you need
 else 
    die "cannot fork: $!" unless defined $pid;
    close READER;
    print WRITER "Child Pid $$ is sending this\n";
    close WRITER;  # this will happen anyway
    exit;

让我们假设我需要我的父母“阅读器”从多个孩子那里获取消息的情况,是否可以在不保留管道列表的情况下做到这一点,每个孩子一个?我无法在父级中关闭 WRITER,因为下一个子级将无法获得有效的句柄来写入。我还需要父级继续其常规操作,而不是阻止来自管道的任何客户端数据。

我需要的伪代码:

# parent code
pipe (READER, WRITER)
fork_random_number_of_children(READER,WRITER)

on_some_tick => 
    my $data = read_from(READER, non_blocking)
    if (data) print "Hey some child sent me: $data"
    else print "No data, going back life"
    do_other_things_before_next_tick()


child_job(R,W)  # lets assume this is called for each child fork
  close (R); # no problem, its a copy
  sleep (random duration)
  print W, "Message from child with pid $$"
  exit 0

【问题讨论】:

一个简单的服务器可以很好地服务吗? (特别是那些回调。)在我看来,这位慢跑的教皇可能正在推动它。 【参考方案1】:

我认为没有必要在父级中close WRITER。这可能是一个很好的做法,但是由于在关闭它之后您不能为新的子进程重用相同的管道,所以这是一个不这样做的好借口。如果在完成启动所有子进程之前保持WRITER 处于打开状态,则可以将管道与多个子进程一起使用。这是一个概念证明:

use IO::Handle;
use POSIX ':sys_wait_h';
pipe(READER,WRITER);
WRITER->autoflush(1);

sub child_process 
    my $stage = shift;
    close READER;  # also a best but optional practice
    srand($$);
    do 
        sleep 1 + 5*rand();
        print WRITER "Child Pid $$ ($stage) is sending this\n";
     while (rand > 0.5);
    exit;


# initial set of children
for (my $i=0; $i<5; $i++) 
    if (fork() == 0) 
        child_process("LAUNCH");
    


# parent
my ($rin,$rout) = ('');
vec($rin,fileno(READER),1) = 1;
while (1) 
     # non-blocking read on pipe
     my $read_avail = select($rout=$rin, undef, undef, 0.0);
     if ($read_avail < 0) 
         if (!$!EINTR) 
             warn "READ ERROR: $read_avail $!\n";
             last;
         
      elsif ($read_avail > 0) 
         chomp(my $line = <READER>);
         print "Read in Parent $$: '$line'\n";
      else 
         print STDERR "No input ... do other stuff\n";
         # start some run-time child processes
         if (time-$^T > 5 && time-$^T < 10) 
             # launch a few more children in the middle of the program
             if (fork() == 0) 
                 child_process("RUN");
             
         
         sleep 1;
     
     last if waitpid(-1,&WNOHANG) < 0;    # no more children are alive

close WRITER;  # now it is safe to do this ...

【讨论】:

谢谢。进行更多测试 - 似乎按我想要的方式工作!你说这是一个概念证明。作为练习,我需要调查哪些关键问题? 首先,您应该检查诸如pipe 之类的系统调用的返回值以及fork 是否返回undef 以指示失败。 使用词法文件句柄而不是全局裸词通常是一个好主意,以防止与其他地方的代码发生冲突,这也意味着它们会在超出范围时自动关闭,这样更易​​于管理。 pipe(my $reader, my $writer) or die "pipe failed: $!"; 你也应该使用strictwarnings 您也可以使用像Mojo::IOLoop 或IO::Async 这样的事件循环,它们旨在完成这类事情(在类似的低级别,或者自己管理所有内容),而不是管理一个自己选择循环和waitpid。如果你有兴趣,我可以举个例子。 二阶问题:写入操作的同步,从管道读取太慢导致管道填充和写入器被阻塞,检查所有函数调用的返回值是否有错误。 @zdim - 在您确定不会再产生任何将使用写入端的子级之前,您无法关闭父级中的写入端。

以上是关于perl:一个父母,许多孩子 - 父母中有一个管道阅读器?的主要内容,如果未能解决你的问题,请参考以下文章

带子进程的双向管道

命名管道,使用 Fork()

Apache 不会在新的 Xampp 安装上启动:管道已结束。孩子:无法从父母那里读取套接字数据

使用谓词在 Core Data 中获取父母的所有孩子

孩子喜欢打人父母要怎么引导?听听专家的小建议

CoreData 获取孩子的父母