URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 7563
[ Назад ]

Исходное сообщение
"perl pipes"

Отправлено iliya , 26-Авг-08 19:03 
Добрый день.
Возникла проблемма с pipe в перле.
есть код, в котором происходит fork на два процесса. в дочернем процессе происходит обработка некоторых команд и через pipe данные передаются родителю. примерно так
pipe(PARENT,CHILD);
PARENT->autoflush(1);
my @arr=();
$pid=fork;

if($pid==0)
{
  close $CHILD;
здесь формируется массив @arr
  foreach my $send_msg (@arr)
  {  print PARENT $send_msg;
  }
   close PARENT;
   exit;
}
else
{
   close PARENT;
   while(my $recv_msg=<CHILD>)
   {
      push @arr,$recv_msg;
   }
  waitpid($pid,0);
}
проблема собственно в том, что при больших размерах массива @arr(килобайт 80) происходит зависание. точнее происходит следующее: чилд в цикле закладывает в пайп данные, после чего завершается. парент вычитывает данные из пайпа после завершения чилд процесса. при больших объемах информации переполняется пайп и чилд виснет. парент ждет завершения чилда и тоже висит.

необходимо реализовать такую схему: чилд положил данные в пайп, парент увидел что данные пришли вычитал их, опустил к примеру семафор, чилд увидел, что семафор опущен и положил еще кусочек данных в пайп. таким образом переполнения не произойдет никогда.
НО. я не пойму как одновременно заставить работать чилда и парента. у меня получается, что пока чилд не завершит свою работу, парент не начнется.


Содержание

Сообщения в этом обсуждении
"perl pipes"
Отправлено NuINu , 26-Авг-08 23:13 
>[оверквотинг удален]
>$pid=fork;
>
>if($pid==0)
>{
>  close $CHILD;
>здесь формируется массив @arr
>  foreach my $send_msg (@arr)
>  {  print PARENT $send_msg;
>  }
>

pipe READHANDLE,WRITEHANDLE
               Opens a pair of connected pipes like the corresponding system call.  Note that if you set up a loop of piped pro-
               cesses, deadlock can occur unless you are very careful.  In addition, note that Perl's pipes use IO buffering, so you
               may need to set $| to flush your WRITEHANDLE after each command, depending on the application.

как это вообще у тебя работает??? ты печатаешь в хендл для чтения!


"perl pipes"
Отправлено iliya , 27-Авг-08 11:58 
>[оверквотинг удален]
>            
>   cesses, deadlock can occur unless you are very
>careful.  In addition, note that Perl's pipes use IO buffering,
>so you
>            
>   may need to set $| to flush your
>WRITEHANDLE after each command, depending on the application.
>
>как это вообще у тебя работает??? ты печатаешь в хендл для чтения!
>

суть не в том! это я на форуме печатаю в хендл для чтения :)) спасибо за поправку конечно :)
но хотелось бы ответа по теме вопроса :)


"perl pipes"
Отправлено NuINu , 27-Авг-08 13:39 
>[оверквотинг удален]
>>            
>>   may need to set $| to flush your
>>WRITEHANDLE after each command, depending on the application.
>>
>>как это вообще у тебя работает??? ты печатаешь в хендл для чтения!
>>
>
>суть не в том! это я на форуме печатаю в хендл для
>чтения :)) спасибо за поправку конечно :)
>но хотелось бы ответа по теме вопроса :)

У меня ничего не виснет, файл кило 100 улетат мгновенно. ничего не переполняется.
вот слегка модифицированый(до рабочего) твой же код:
#!/usr/bin/perl


use IO::File;
use File::Spec;

use strict;

sub print_arr {
    my @t = @_;
    foreach my $l (@t) {
    print $l;
    }
}

pipe(CHILD, PARENT);
CHILD->autoflush(1);
my @arr=();
my $delim = '---------------------------------------------------------------------------'."\n";
print "Single process work\n";
my $pid=fork;

if($pid==0) {
    #Child work, children process read file, to arr, and send to parent
    close CHILD;
    print "Child process work: $$\n";
    #здесь формируется массив @arr
    open(F_IN, "<", 'arr.txt');
    while(my $t = <F_IN>) {
    push @arr, $t;
    }
    close(F_IN);
    print "Child Process: makes array:\n";
    print "Child Process:".$delim;
    print_arr(@arr);
    print "Child Process:".$delim;
    foreach my $send_msg (@arr){
    print PARENT $send_msg;
    }
    close PARENT;
    exit;
}
else {
    #Parent work, parent process read data from pipe child and make array, after all read print
    print "Parent process work: $$, child: $pid\n";
    $SIG{CHLD} = "IGNORE";
    close PARENT;
    while(my $recv_msg=<CHILD>) {
        push @arr,$recv_msg;
    }
    print "Parent Process:".$delim;
    print_arr(@arr);
    print "Parent Process:".$delim;

    #waitpid($pid,0);
}


"perl pipes"
Отправлено iliya , 01-Сен-08 16:51 
привет.
да, действительно. этот код рабочий!
и если в моем реальном коде оставить один пайп, то тоже все работает. но у меня немного другая ситуация. у меня четыре пайпа и вот примерно так это выглядит:

#!/usr/bin/perl


use IO::File;
use File::Spec;
use POSIX;
use strict;

sub print_arr {
   my @t = @_;
   foreach my $l (@t) {
       print $l;
   }
}

pipe(CHILD, PARENT);
pipe(R1,W1);
pipe(R2,W2);
pipe(R3,W3);
R1->autoflush(1);
R2->autoflush(1);
R3->autoflush(1);
CHILD->autoflush(1);
my @arr=();
my $delim = '---------------------------------------------------------------------------'."\n";
print "Single process work\n";
my $pid=fork;

if($pid==0) {
   #Child work, children process read file, to arr, and send to parent
   close CHILD;close R1;close R2;close R3;
   print "Child process work: $$\n";
   #здесь формируется массив @arr
   print W1 "1";
   open(F_IN, "<", '/var/log/nortel/linuxbase.log');
   while(my $t = <F_IN>) {
       push @arr, $t;
   }
   close(F_IN);
   print "Child Process: makes array:\n";
   print "Child Process:".$delim;
#   print_arr(@arr);
   print scalar @arr;
   print "Child Process:".$delim;
   my $count=1;
   foreach my $send_msg (@arr){
       print PARENT $send_msg;
       print "'$count'{child}\n";$count++;
   }
   close PARENT;
   close W1;close W2;close W3;
   _exit(0);
}
else {
   #Parent work, parent process read data from pipe child and make array, after all read print
   print "Parent process work: $$, child: $pid\n";
   $SIG{CHLD} = "IGNORE";
   close PARENT;
close W1;close W2;close W3;
   my $read1=<R1>;
   print "'$read1'\n";
   while(my $recv_msg=<CHILD>) {
           push @arr,$recv_msg;
   }
   print "Parent Process:".$delim;
#   print_arr(@arr);
   print scalar @arr;
   print "Parent Process:".$delim;
   close CHILD;
   close R1;close R2;close R3;
   waitpid($pid,0);
}

данный код уже висит.
можно как-либо увеличить количество памяти, которое отводиться под пайпы? как я понимаю, оно всегда постоянно и равно, допустим, COUNT_MEM и если открывается пять пайпов то на каждый пайп отводится COUNT_MEM/5 byte.
ps или я несу полную чушь? :)))
please help :)
pps. последнее, говорит child: '602'{child}, тоесть он отправил 602 сообщения в пайп.


"perl pipes"
Отправлено NuINu , 02-Сен-08 10:10 
>[оверквотинг удален]
>}
>
>данный код уже висит.
>можно как-либо увеличить количество памяти, которое отводиться под пайпы? как я понимаю,
>оно всегда постоянно и равно, допустим, COUNT_MEM и если открывается пять
>пайпов то на каждый пайп отводится COUNT_MEM/5 byte.
>ps или я несу полную чушь? :)))
>please help :)
>pps. последнее, говорит child: '602'{child}, тоесть он отправил 602 сообщения в пайп.
>

понятно что висит, вы записали один байт в W1 а в парент процессе ждете что вам придет целая строка из R1. либо пишите "1\n" либо читайте один байт.
и не заморачивайтесь размером буферов. не в них дело.
не знаю как у меня затесался этот код:
pipe(CHILD, PARENT);
CHILD->autoflush(1);
но он совершенно не верен!!!
он просто не имеет смысла! он как раз и устанавливает размер буфера при котором происходит автоматический сброс буферов, при заполнении буфера размером 1.
но этот дескриптор на чтение, и устанавливать там размер буфера бессмысленно.
надо было поставить:
PARENT->autoflush(1);
тогда размер буфера, на запись не превышал бы 1 символа, и данные передавались бы практически сразу.

но поскольку у вас происходит обмен строками все эти установки не особо важны(даже вредны, т.к. излишне нагружают систему)


"perl pipes"
Отправлено iliya , 02-Сен-08 13:51 
>понятно что висит, вы записали один байт в W1 а в парент
>процессе ждете что вам придет целая строка из R1. либо пишите
>"1\n" либо читайте один байт.

СПАСИБО! человеческое и огромное :)))) я б быстрее организовал еще пару процессов и семафоры, что б родитель сигнализировал, что получены данные и чилд после этого отсылал следующую порцию, чем понял бы что дело в строках :)
лучше меньше, да лучше(с) как говорил вождь :)

>и не заморачивайтесь размером буферов. не в них дело.

угу.

>не знаю как у меня затесался этот код:
>pipe(CHILD, PARENT);
>CHILD->autoflush(1);
>но он совершенно не верен!!!
>он просто не имеет смысла! он как раз и устанавливает размер буфера
>при котором происходит автоматический сброс буферов, при заполнении буфера размером 1.

вот здесь я не очень понял, тоесть при таком подходе в пайп будет передаваться по одному байту за раз?

>но этот дескриптор на чтение, и устанавливать там размер буфера бессмысленно.
>надо было поставить:
>PARENT->autoflush(1);

это понятно :)))


Спасибо!


"perl pipes"
Отправлено NuINu , 02-Сен-08 14:24 

>>не знаю как у меня затесался этот код:
>>pipe(CHILD, PARENT);
>>CHILD->autoflush(1);
>>но он совершенно не верен!!!
>>он просто не имеет смысла! он как раз и устанавливает размер буфера
>>при котором происходит автоматический сброс буферов, при заполнении буфера размером 1.
>
>вот здесь я не очень понял, тоесть при таком подходе в пайп
>будет передаваться по одному байту за раз?

Скажем, так, я наврал. и перепутал логическую перемнную, с размером.


"perl pipes"
Отправлено NuINu , 02-Сен-08 14:36 

>>он просто не имеет смысла! он как раз и устанавливает размер буфера
>>при котором происходит автоматический сброс буферов, при заполнении буфера размером 1.
>
>вот здесь я не очень понял, тоесть при таком подходе в пайп
>будет передаваться по одному байту за раз?
>

суть (auttoflush)в том что данные будут передаваться каждый раз после print(вообщем после записи в пайп). если вывел 1 байт, передастся 1 байт.
вобщем если нужно поставь.

только что бы это увидеть нужно вывод(отладочный) также сделать не буферизированным, если отладка идет stdout то так:
STDOUT->autoflush(1);


"perl pipes"
Отправлено iliya , 02-Сен-08 17:20 
еще раз спасибо за разъяснения :)