2009-06-09 8 views
3

Récemment, j'ai eu un problème en utilisant (pipe | -) quand je voulais communiquer entre deux processus. Fondamentalement, le processus enfant ne pouvait pas traiter STDIN aussi vite qu'il était rempli par le parent. Cela a amené le parent à attendre que STDIN soit libre et l'ait ralenti.Comment puis-je empêcher le parent de bloquer en écrivant à un enfant?

Quelle est la taille maximale de STDIN et est-il possible de le modifier? Si oui, quelle est la taille de la meilleure pratique?

Voici quelques exemple de code pour montrer ce que je veux dire:

if ($child_pid = open($child, "|-")) 
{ 
    $child->autoflush(1); 

    # PARENT process 
    while (1) 
    { 

      # Read packet from socket save in $packet 
      process_packet($packet); 

      # forward packet to child 
      print $child $packet; 
    } 
} 
else 
{ 
    die "Cannot fork: $!" unless defined $child_pid; 
    # CHILD process 
    my $line; 

    while($line = <STDIN>) 
    { 
     chomp $line; 
     another_process_packet($line); 
    } 
} 

Dans cet exemple another_process_packet plus lent que process_packet. La raison pour laquelle j'écris le code comme ceci est, je veux utiliser les mêmes données vient du socket et l'obtenir une fois.

Merci d'avance.

+0

Pourriez-vous poster des versions raccourcies de vos scripts de producteur et de consommateur? –

+0

cerveau d foy, merci pour les changements. C'est un peu embarrassant d'avoir autant d'erreurs ... – Omid

Répondre

6

Vous pouvez bien sûr mettre en mémoire tampon dans le processus parent et seulement écrire à l'enfant lorsque le fd de l'enfant est accessible en écriture (c'est-à-dire que l'écriture ne bloque pas). Vous pouvez faire vous-même avec les bons args à SYSWRITE, ou utiliser une boucle d'événement:

use AnyEvent; 
use AnyEvent::Handle; 

# make child, assume you write to it via $fh 

my $done = AnyEvent->condvar; 
my $h = AnyEvent::Handle->new(fh => $fh); 

while(you do stuff){ 
    my $data = ...; 
    $h->push_write($data); # this will never block 
} 

$h->on_drain(sub { $done->send }); 
$done->wait; # now you block, waiting for all writes to actually complete 

Edit: Ceci était non testé, mais je l'ai testé et il fonctionne. (J'ai utilisé perl -ne "sleep 1; print $_" comme enfant lent.) Les écritures continuent pendant la boucle while, si possible, mais ne bloquent jamais la boucle. À la fin, vous bloquez réellement jusqu'à ce que toutes les écritures soient terminées.

Mes scripts de test sont sur gist.github: http://gist.github.com/126488

Vous pouvez voir comment les blocs enfants la boucle de blocage, mais comment il ne bloque pas la boucle non-bloquant. Evident quand vous mettez cette façon;)

(Enfin, en règle générale,. Si vous êtes en interaction avec le réseau ou avec d'autres processus, vous devriez probablement utiliser une boucle d'événement)

+0

Merci BTW, mais je ne veux pas bloquer les parents pour une raison quelconque. Probablement j'ai besoin de lire plus sur AnyEvent. Merci. – Omid

+0

Eh bien, ne bloquez pas alors. Je bloque seulement à la fin parce que le processus est sur le point de sortir, et si vous quittez avec des données non écrites, l'enfant ne le verra jamais. S'il s'agit d'un processus de longue durée, vous n'aurez pas besoin de bloquer. – jrockway

+0

Eh bien, il semble que AnyEvent :: Handle marche plutôt bien, mais j'ai encore quelques problèmes. Après que j'arrête l'enfant pendant un moment pour que le tampon STDIN soit plein, parent pousse les données dans wbuf et c'est ce qu'il supposait faire mais après que je libère l'enfant, alors l'enfant ne traite que ces données. et ne jamais toucher wbuf. Ensuite, pour forcer wbuf à pousser ses données dans STDIN, j'utilise on_drain (sub {$ done-> send}) et une ligne après j'utilise $ done-> recv pour faire attendre les parents, mais pendant ce moment je perds des données car elles viens de la douille. Une idée de comment je peux gérer ce problème ??? – Omid

0

processus handle contient une fonction membre nommée 'blocking'. Réglez simplement le blocage sur 0 et le processus parent ne sera pas bloqué.

if ($child_pid = open($child, "|-")) 
{ 
    $child->blocking(0); # Key to the solution. 
    $child->autoflush(1); 

    # PARENT process 
    while (1) 
    { 

      # Read packet from socket save in $packet 
      process_packet($packet); 

      # forward packet to child 
      print $child $packet; 
    } 
} 
else 
{ 
    die "Cannot fork: $!" unless defined $child_pid; 
    # CHILD process 
    my $line; 

    while($line = <STDIN>) 
    { 
     chomp $line; 
     another_process_packet($line); 
    } 
} 
+0

Thanks Cheok :) – Omid

+0

L'enfant ne fonctionne pas correctement. – Omid

Questions connexes