如何使用 IPC::Open2 过滤大量数据?

Posted

技术标签:

【中文标题】如何使用 IPC::Open2 过滤大量数据?【英文标题】:How to filter a lot of data with IPC::Open2? 【发布时间】:2011-09-02 13:34:36 【问题描述】:

我的任务是使用外部实用程序(addr2line)从 perl 脚本中过滤一些数据。数据量相当大。我需要将大量数据打印到程序的stdin 并读回大量数据(从程序的stdout 到我的脚本中)。

现在我用IPC::Open2 来做这件事,但我不会混合阅读和写作。这合法吗? Open2 会在管道中缓冲任意大小的数据吗?

我的代码:

my $cmd="addr2line -e $prog_name ";
use IPC::Open2;
local (*Reader, *Writer);
my $pid = open2(\*Reader, \*Writer, $cmd);
for(@requests)   # this array is HUGE, 100s of thousands of entries
    print Writer "$_\n";

close Writer;  
for(@requests) 
    $function_name = <Reader>;
    $filesource = <Reader>;
   #... store ..

close Reader;
waitpid($pid,0);

【问题讨论】:

好消息。您可以混合写入一行和读取两行,因为 addr2line 执行 "fgets(stdin)" 和 "fprintf;fprintf;fflush(stdout);" 【参考方案1】:

是的,您会遇到程序编写方式的缓冲区容量限制。您的输入缓冲区 (Reader) 将填满并阻止执行您的外部程序。

混合读取和写入会有所帮助,因为您清空输入缓冲区的速度与外部程序填充它的速度大致相同。

另一件有用的事情是使用文件而不是管道或套接字进行进程间通信(就像IPC::Open2 所做的那样)。那么您将仅受可用磁盘空间量的限制。你可以自己做,虽然Forks::Super 默认使用文件进行 IPC。

use Forks::Super 'open2';

...
my ($Reader,$Writer,$pid) = open2(@command);
for (@requests)  print $Writer "$_\n" 
close $Writer;
for (@requests)  ... read ... 
close $Reader;
waitpid $pid,0;

【讨论】:

是 Fork::Super 标准还是 CPAN?该程序是来自 binutils 的 addr2line 混合阅读和写作会有所帮助,因为 addr2line 旨在与管道一起使用。它为每个输入行刷新其输出。它会在循环中执行 fgets 来读取输入。【参考方案2】:

管道的尺寸有限。你的方法会陷入僵局

  Parent                 Child
  ------                 -----
  ...                    ...
                         Wait for data in Writer
  Put data in Writer
                         Read data from Writer
                         Put data in Reader
                         Wait for data in Writer
  Put data in Writer
                         Read data from Writer
                         Put data in Reader
                           => Blocks cause Reader is full
  Put data in Writer
  Put data in Writer
  ...
  Put data in Writer
  Put data in Writer
    => Blocks cause Writer is full

一种可能的解决方案:

use strict;
use warnings;
use threads;
use IPC::Open2 qw( open2 );

my @cmd = ("addr2line", "-e", $prog_name);

local (*Reader, *Writer);
my $pid = open2(\*Reader, \*Writer, @cmd);

my $thread = async 
   for (;;) 
       $function_name = <Reader>;
       last if !defined($function_name);
       $filesource = <Reader>;
       #... store ..
   

   close Reader;
;


   my @requests = ...;

   for(@requests)   # this array is HUGE, 100s of thousands of entries
      print Writer "$_\n";
   

   close Writer;


$thread->join();
waitpid($pid, 0);

另外,IPC::Run 也有工具可以让这一切变得简单。

统一的方法是使用IO::Select,但这真的很痛苦。

【讨论】:

$thread = async - 执行此操作需要什么最小的 perl 版本? @osgx,5.6,我想?不过,您需要 Perl 的线程化构建。 @osgx, perl -v 会告诉你,例如i686-linux-thread-multi 我尝试了上述解决方案,但偶然发现了以下问题。由于 Perl 的线程进程状态复制,close() 不会传播到进程,因此读取端永远不会看到 EOF。我最终使用了 IPC::Run,它似乎在内部使用了 select(),它的工作原理就像一个魅力。 @Diomidis Spinellis ,是的,我绝对推荐使用 IPC::Run 而不直接使用管道

以上是关于如何使用 IPC::Open2 过滤大量数据?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 IO::Pty 和 Expect 模块可以帮助解决 open2 的障碍?

IPC::Open3 在 Apache 下运行失败

如何获取大量 GET 参数并根据它们干净地过滤查询?

如何从数据库中获取大量数据并通过输入输入将其显示到下拉列表过滤器中,但只能从数据库中选择

如何在android中获得这个Json响应?我在服务器上获得大量数据,所以我想过滤它

如何根据某些过滤条件从 bigquery 导出数据