Как отфильтровать много данных с помощью IPC :: Open2? - PullRequest
2 голосов
/ 02 сентября 2011

Моя задача - отфильтровать некоторые данные из сценария Perl с помощью внешней утилиты (addr2line).Размер данных довольно большой.Мне нужно напечатать много данных в 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);

Ответы [ 2 ]

3 голосов
/ 03 сентября 2011

Трубы имеют ограниченные размеры. Ваш подход заблокирует

  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 , но это настоящая боль.

3 голосов
/ 02 сентября 2011

Да, вы столкнетесь с ограничениями емкости буфера при написании вашей программы.Ваш входной буфер (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;
...