2010-10-06 16 views
5

Sto scrivendo un programma che forca più processi figlio e mi piacerebbe che tutti questi processi figli fossero in grado di scrivere righe su STDERR e STDOUT senza il l'output è confuso. Non sto facendo niente di speciale, semplicemente emettendo righe che terminano con una nuova linea (che, almeno a mio avviso, sarebbe un'operazione atomica per Linux). Da perlfaq:fork() e STDOUT/STDERR alla console da processi figlio

Sia il processo principale che quello in background (il processo "figlio") condividono gli stessi filehandle STDIN, STDOUT e STDERR. Se entrambi provano ad accedervi contemporaneamente, possono accadere cose strane. Potresti voler chiudere o riaprire questi per il bambino. È possibile aggirare questo problema aprendo una pipe (vedi open) ma su alcuni sistemi ciò significa che il processo figlio non può sopravvivere al genitore.

Dice che dovrei "chiudere o riaprire" questi filehandle per il bambino. La chiusura è semplice, ma cosa significa "riaprire"? Ho provato qualcosa di simile da dentro il mio processi figlio e non funziona (l'uscita viene comunque confuso):

open(SAVED_STDERR, '>&', \*STDERR) or die "Could not create copy of STDERR: $!"; 
close(STDERR); 

# re-open STDERR 
open(STDERR, '>&SAVED_STDERR') or die "Could not re-open STDERR: $!"; 

Quindi, cosa sto facendo di sbagliato in questo? Come sarebbe l'esempio di tubo che allude? C'è un modo migliore per coordinare l'output da più processi insieme alla console?

risposta

9

Le scritture su un filehandle sono NON atomico per STDOUT e STDIN. Ci sono casi speciali per cose come i fifo, ma questa non è la tua situazione attuale.

Quando dice riaprire STDOUT ciò che significa è "creare una nuova istanza STDOUT" Questa nuova istanza non è la stessa di quella del genitore. È come si possono avere più terminali aperti sul proprio sistema e non tutti gli STDOUT vanno nello stesso posto.

La soluzione pipe connetterebbe il figlio al genitore tramite una pipe (come | nella shell) e avresti bisogno che il genitore abbia letto la pipe e multiplex l'output stesso. Il genitore sarebbe responsabile della lettura dalla pipe e assicurandosi che non interconnassi l'output dalla pipe e l'output destinato allo STDOUT del genitore contemporaneamente. C'è un esempio e scrivi here di tubi.

A snippit:

use IO::Handle; 

pipe(PARENTREAD, PARENTWRITE); 
pipe(CHILDREAD, CHILDWRITE); 

PARENTWRITE->autoflush(1); 
CHILDWRITE->autoflush(1); 

if ($child = fork) { # Parent code 
    chomp($result = <PARENTREAD>); 
    print "Got a value of $result from child\n"; 
    waitpid($child,0); 
} else { 
    print PARENTWRITE "FROM CHILD\n"; 
    exit; 
} 

vedere come il bambino non scrivere a stdout, ma piuttosto usa il tubo per inviare un messaggio al genitore, che fa la scrittura con il suo stdout. Assicurati di dare un'occhiata perché ho omesso cose come la chiusura degli handle di file non necessari.

+0

Ho finito per andare con IO :: Pipe e AnyEvent per i tubi e la selezione di IO, ma sembra funzionare. Grazie – mpeters

1

Anche se questo non aiuta la vostra sortevolezza, mi ci è voluto molto tempo per trovare un modo per avviare un processo figlio che può essere scritto dal processo padre e avere lo stderr e lo stdout del processo figlio inviati direttamente sullo schermo (questo risolve i problemi di blocco che potresti avere quando provi a leggere da due FD diversi senza usare qualcosa di particolare come selezionare).

Una volta ho capito, la soluzione è stata banale

my $pid = open3(*CHLD_IN, ">&STDERR", ">&STDOUT", 'some child program'); 
# write to child 
print CHLD_IN "some message"; 
close(CHLD_IN); 
waitpid($pid, 0); 

Tutto da "qualche programma bambino" sarà emesso per output/error, e si può semplicemente pompare dati scrivendo a CHLD_IN e la fiducia che esso bloccherà se il buffer del bambino si riempie. Ai chiamanti del programma principale sembra tutto come stderr/stdout.

Problemi correlati