2010-02-26 4 views
6

Tenir compte le script suivant:Pourquoi mon processus parent ne voit-il pas la sortie de l'enfant jusqu'à sa sortie?

use IO::File; 
$| = 1; 
my ($handle, $pid) = myPipe(); 
if ($pid == 0) { 
    print "$$"; 
    sleep 5; 
    exit; 
} 

print "child: ".<$handle>."\n"; 

sub myPipe { 
    my $handle = new IO::File(); 
    my $pid = open($handle, "-|"); 
    return ($handle, $pid); 
} 

Dans ce cas, le « enfant: » message ne semble pas pendant 5 secondes après que le processus commence. Si je supprime l'appel de sommeil de l'enfant fourchu, alors il imprime immédiatement. Pourquoi l'enfant forké doit-il sortir pour que le tuyau vire au parent?

Répondre

11

Il y a deux problèmes. Premièrement, le processus enfant met en tampon sa sortie; et deuxièmement, le processus parent utilise l'opérateur <>, qui se bloque jusqu'à ce qu'une ligne complète soit disponible ou jusqu'à la fin du fichier.

Ainsi, une façon d'obtenir le résultat que vous attendiez est d'avoir le processus enfant fermer son flux de sortie immédiatement après l'écriture:

if ($pid == 0) { 
    print "$$"; 
    close STDOUT; 
    sleep 5; 
    exit; 
} 

Une autre façon est d'ajouter une nouvelle ligne à la sortie du processus enfant, puis rincer le courant:

if ($pid == 0) { 
    print "$$\n"; 
    STDOUT->flush; # "close STDOUT;" will work too, of course 
    sleep 5; 
    exit; 
} 

la chasse d'eau est nécessaire parce que les tuyaux sont (en général) non tamponnée, plutôt que la ligne tampon sous forme de courants connectés à la borne sont habituellement.

Une troisième solution consiste à fixer le courant de sortie du processus enfant à autoflush:

if ($pid == 0) { 
    $| = 1; 
    print "$$\n"; 
    sleep 5; 
    exit; 
} 
+0

Merci pour la réponse approfondie. J'ai réalisé plus tard qu'utiliser <> aurait nécessité un nouveau caractère de ligne. – dromodel

2

Le rinçage du tuyau ne s'effectue pas selon un programme fixe. Les deux seules manières de forcer le vidage du tube consistent à quitter le processus enfant (ce que vous faites actuellement) ou à appeler explicitement le flush. Vous pouvez faire votre poignée pour débusquer en Perl en effectuant l'une des opérations suivantes:

  • Ajout d'un \n à la fin du message de l'enfant, qui (habituellement) provoquer le tuyau pour rincer
  • Réglage $|-1 , qui provoque le vidage automatique du handle de fichier actuellement sélectionné
  • En utilisant IO::Handle et en appelant $handle->flush.
  • En utilisant IO::Handle et la mise en $handle->autoflush = 1
+2

Rinçage du tuyau ne sont pas manipulés par le noyau. La mise en mémoire tampon est une fonctionnalité utilisateur! –

3

Sur certains (la plupart?) Des systèmes, des tuyaux utilisent tampon E/S par défaut. Mettez un

$handle->autoflush(1); 

instruction dans votre myPipe fonction. Mais même lorsque la mise en mémoire tampon est désactivée, Perl ne vide toujours pas, sauf après un retour à la ligne. Ainsi, vous voudrez peut-être aussi que votre processus fils inclue une nouvelle ligne dans la sortie.


Mise à jour: Test de votre code (Cygwin, perl 5.10.0, YMMV), je vois le problème est un manque de saut de ligne dans la sortie de l'enfant, pas si autoflush est explicitement activée lorsque le tuyau est créé.

+1

Ce n'est pas tout à fait exact.Un handle est non-buffered (vider après chaque impression) si autoflush est activé, ligne-buffered (vider après chaque nouvelle ligne) si autoflush est éteint et le handle est ouvert à un tty, et block-buffered (vider quand un tampon , généralement de quelques kB, est plein) sinon. – hobbs

+1

Re: mise à jour - voir la réponse de Sean. La nouvelle ligne ne compte pas en raison de la mise en mémoire tampon, mais parce que l'opération readline/'<>' dans le parent * doit lire une nouvelle ligne avant qu'elle ne revienne * - sauf à EOF :) – hobbs

+0

@hobbs - Merci pour la clarification. Dans cet exemple particulier, l'appel à «<$handle>» dans le parent ne renvoie pas tant qu'il n'y a pas de saut de ligne dans l'entrée ou que l'entrée est fermée. Donc, la nouvelle ligne est toujours le problème, mais pour une raison légèrement différente de ce que je pensais. Vous pourriez faire quelque chose de funky avec '$ /', je suppose. – mob

Questions connexes