2010-10-06 15 views
5

Piszę program, który przekazuje wiele procesów potomnych i chciałbym, aby wszystkie te procesy potomne były w stanie pisać linie do STDERR i STDOUT bez wynik jest zniekształcony. Nie robię nic nadzwyczajnego, tylko emituję linie kończące się nową linią (co, przynajmniej w moim rozumieniu, byłoby operacją atomową dla Linuksa). Z Perlfaqa jest napisane:fork() i STDOUT/STDERR na konsolę z procesów potomnych

Zarówno proces główny, jak i obiekt w tle (proces "potomny") mają te same uchwyty plików STDIN, STDOUT i STDERR. Jeśli obaj spróbują uzyskać do nich dostęp od razu, mogą się zdarzyć dziwne rzeczy. Możesz je zamknąć lub ponownie otworzyć dla dziecka. Możesz obejść to, otwierając potok (patrz otwarte), ale w niektórych systemach oznacza to, że proces potomny nie może przeżyć nadrzędnego.

Mówi, że powinienem "zamknąć lub ponownie otworzyć" te uchwyty plików dla dziecka. Zamknięcie jest proste, ale co oznacza "ponowne otwarcie"? Próbowałem coś takiego od wewnątrz moich procesów potomnych i nie działa (wyjście nadal pobiera zniekształcone):

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: $!"; 

Więc, co robię źle z tym? Jak mógłby wyglądać przykład rury? Czy istnieje lepszy sposób skoordynowania danych wyjściowych z wielu procesów na konsoli?

Odpowiedz

9

Zapisywanych do uchwytu pliku są NOT atomowe dla STDOUT i STDIN. Istnieją specjalne przypadki dotyczące rzeczy typu fifos, ale to nie jest twoja obecna sytuacja.

Kiedy mówi ponownie otworzyć STDOUT, co to znaczy "Utwórz nową instancję STDOUT" Ta nowa instancja nie jest taka sama jak ta z rodzica. W ten sposób można mieć wiele terminali otwartych w systemie i nie wszystkie STDOUT iść w to samo miejsce.

Rozwiązanie rurociągu łączyłoby dziecko z rodzicem za pośrednictwem potoku (np. | W powłoce), a użytkownik musiałby odczytywać obiekt nadrzędny z rury i multipleksować dane wyjście. Jednostka nadrzędna byłaby odpowiedzialna za odczytanie z potoku i zapewnienie, że nie będzie on przeplatał danych wyjściowych z rury i wyjścia przeznaczonego do STDOUT jednostki macierzystej w tym samym czasie. Jest przykład i zapis here rur.

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

Zobacz jak dziecko nie pisać na standardowe wyjście, lecz korzysta z rury, aby wysłać wiadomość do rodzica, który czyni pisanie z jego standardowe wyjście. Pamiętaj, aby pominąć takie rzeczy jak zamykanie niepotrzebnych uchwytów plików.

+0

Skończyłem z IO :: Pipe i AnyEvent dla rur i wyboru IO, ale to wydaje się działać. Dzięki – mpeters

1

Chociaż nie pomaga to twojemu garbowaniu, zajęło mi dużo czasu, aby znaleźć sposób na uruchomienie procesu potomnego, który może być zapisany w procesie nadrzędnym i aby stderr i stdout procesu potomnego zostały wysłane bezpośrednio na ekranie (to rozwiązuje nieprzyjemne problemy z blokowaniem, które możesz mieć, próbując odczytać z dwóch różnych FD bez użycia czegoś podobnego do wybranej).

Raz zorientowaliśmy się, rozwiązanie było banalne

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

Wszystko z „jakiś program dziecko” będzie emitowany do stdout/stderr, a może po prostu pompować danych pisząc na CHLD_IN i ufać, że to Blokuje się, jeśli bufor dziecka się zapełni. Dla dzwoniących programu nadrzędnego wszystko wygląda jak stderr/stdout.

Powiązane problemy