2010-02-26 6 views
6

Si consideri il seguente script:Perché il mio processo genitore non vede l'output del figlio finché non esce?

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); 
} 

In questo caso, il "bambino:" messaggio non viene visualizzato per 5 secondi dopo l'avvio del processo. Se rimuovo la chiamata a riposo dal bambino biforcuto, viene stampata immediatamente. Perché il bambino biforcuto deve uscire perché la pipa venga scaricata dal genitore?

risposta

11

Ci sono due problemi. Innanzitutto, il processo figlio sta eseguendo il buffering del suo output; in secondo luogo, il processo padre utilizza l'operatore <>, che blocca fino a quando una linea completa è disponibile o fino alla fine del file.

Quindi, un modo per ottenere il risultato che ti aspettavi è quello di avere il processo figlio chiudere il suo flusso di uscita subito dopo la scrittura:

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

Un altro modo è quello di aggiungere una nuova riga alla uscita del processo figlio, poi lavare il flusso:

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

il filo è necessaria perché i tubi sono (tipicamente) bufferizzato, anziché linea tamponata come flussi collegati al terminale di solito sono.

Una terza alternativa è quella di impostare flusso di output del processo figlio il flush automatico:

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

Grazie per la risposta completa. Mi resi conto in seguito che l'utilizzo di <> avrebbe richiesto un nuovo carattere di linea. – dromodel

2

Il lavaggio del tubo non avviene in base a un programma fisso. Gli unici due modi in cui è possibile forzare lo svuotamento del pipe sono l'uscita dal processo figlio (che è ciò che si sta facendo ora) o chiamando esplicitamente lo flush. Si può causare la maniglia per irrigare in Perl effettuando una delle seguenti operazioni:

  • Aggiunta di un \n alla fine del messaggio del bambino, che (di solito) causare il tubo per irrigare
  • Impostazione $|-1 , che fa sì che il filehandle attualmente selezionato esegua il flush automatico
  • utilizzando IO::Handle e chiamando $handle->flush.
  • Utilizzando IO::Handle e impostazione $handle->autoflush = 1
+2

Lavaggio del tubo non è gestito dal kernel. Il buffering è una funzionalità per gli utenti! –

3

Su alcuni (la maggior parte?) Dei sistemi, tubi di utilizzare il buffer di I/O di default. Mettere una dichiarazione

$handle->autoflush(1); 

nella funzione myPipe.

Ma anche quando il buffering è disattivato, Perl continua a non sparare, tranne dopo una nuova riga. Quindi potresti anche volere che il tuo processo figlio includa una nuova riga nell'output.


Aggiornamento: Testare il codice (Cygwin, Perl 5.10.0, YMMV), vedo il problema è la mancanza della nuova linea nell'output bambino, non se autoflush è esplicitamente attivata quando il tubo è stato creato.

+1

Non è giusto.Un handle non è bufferato (flush dopo ogni stampa) se è attivato l'autoflush, bufferizzato dalla linea (flush dopo ogni newline) se autoflush è disattivato e l'handle è aperto a un tty e buffer-block (flush quando un buffer , di solito di pochi kB, è pieno) altrimenti. – hobbs

+1

Ri: aggiornamento - vedere la risposta di Sean. La nuova riga non è importante a causa del buffering, ma perché l'operazione readline/'<>' nel parent * deve leggere una nuova riga prima che essa ritorni * - eccetto EOF :) – hobbs

+0

@hobbs - Grazie per i chiarimenti. In questo particolare esempio, la chiamata a '<$handle>' nel parent non ritorna fino a quando non vi è una nuova riga nell'input o l'input è chiuso. Quindi la nuova riga è ancora il problema, ma per un motivo leggermente diverso da quello che pensavo. Potresti fare qualcosa di funky con '$ /', suppongo. – mob