2017-01-13 14 views
8

Piszę program C++, który wcześnie widzi i gdzie używam std :: cout i std :: cin w procesach potomnych i nadrzędnych. Z jakiegoś powodu w Linuksie cin nie działa w procesie potomnym; nigdy nie prosi o żadne dane wejściowe. Zabawne jest to, że ten sam program działa dobrze na Macu. Czy ktoś wie, dlaczego tak się dzieje? Dzięki.C++: używanie cin na proces potomny

+0

Dobre pytanie. Może proces potomny nie kieruje wyjścia do twojej powłoki lub czegoś w tym stylu. –

+3

Czy masz krótki przykładowy program do przetestowania? – qxz

+0

http://stackoverflow.com/a/13738789/560648? –

Odpowiedz

6

To, co zaobserwowałeś, wynika z modelu fork i exec . Wszystkie deskryptory plików są kopiowane zgodnie z oczekiwaniami, ale kolejność pierwszeństwa, gdy dwa procesy odczytują z pojedynczego deskryptora, jest niezdefiniowana. . To, że są rodzicem i dzieckiem, nie ma znaczenia, gdy fork() powróci.

W rezultacie Twoja sytuacja jest jeszcze gorsza niż po prostu zależna od wdrożenia. Możesz uzyskać dwa różne wyniki w systemie SAME.

Twój scenariusz to warunek wyścigu. Która z dwóch kopii programu (macierzysta lub podrzędna) określa, który znak jest w takich przypadkach zależny od wielu szczegółów związanych z czasem. Nawet to, jakich zasobów wymagają inne procesy w twoim systemie, może odegrać obserwowane zachowanie.

Operacje odczytu nie mają charakteru atomowego z natury .
Jeśli możesz odczytać tę samą postać z tego samego strumienia zarówno przez rodzica, jak i dziecko w dowolnym systemie operacyjnym, system operacyjny nie chroni poprawnie przed tym stanem wyścigu i jest to problem jądra, który powinien zostać zgłoszony jako możliwy błąd .

Możesz rozwiązać tę niejednoznaczność funkcjonalną za pomocą semafora lub innych metod synchronizacji. Jeśli taki mechanizm zostałby zastosowany właściwie do zagwarantowania odczytów atomowych, możesz osiągnąć bezpieczeństwo wątku (lub w tym przypadku bezpieczeństwo procesu), ale możesz nadal nie mieć tego, czego chcesz.

Klasycznym rozwiązaniem może być decyzja, który z dwóch procesów, które chcesz przeczytać std :: cin, i zamknij std :: cin w drugim procesie. Standardowym mechanizmem do tego celu jest przetestowanie liczby całkowitej zwracanej z wywołania fork. Jeśli (fork() == 0) jesteś w dziecku. (Przykłady są podane w dokumentacji fork).

Jeśli potrzebujesz wartości w obu procesach, możesz użyć pipe() i dup2() przed widelcem i właściwego close() w każdym procesie, aby przesłać strumieniowo kopię postaci od głównego czytelnika do drugiego. To jest wzór projektu proxy. Jeśli różne typy wiadomości powinny być obsługiwane przez różne procesy, możesz również chcieć wdrożyć schemat projektowania łańcucha odpowiedzialności.

Warto zauważyć, że deskryptory plików wyjściowych zapakowane przez std :: cout i std :: cerr nie mają ryzyka związanego z wyścigiem i można mieszać dane wyjściowe od rodzica i dziecka .

.......

[1] Standardy POSIX sięga początku UNIX, jak daleko wstecz jak PDP-11.

[2] Open Group Baza Specyfikacja IEEE Std Wydanie 7 1003.1-2008, 2016 Edition strona podręcznika dla pread i odczytać stany „The standardowe deweloperom rozważyć dodanie wymogów atomowości do rury lub FIFO, ale uznał, że ze względu z natury rur i FIFO nie można zagwarantować atomowości odczytów {PIPE_BUF} lub jakiejkolwiek innej wielkości, która byłaby pomocna w przenoszeniu aplikacji."

[3] Sąsiadujące czyta tego samego komunikatu bajtów prawdopodobnie narusza kryteria standardów. Nie powinno być semafor system lub inne krytyczne ochrony kodu po przejęciu bajt lub znak ze strumienia wejściowego tak, że bajt lub Czytaj znak jest odrzucana, zanim można ponownie przeczytać.

[4] Zapis do tego samego strumienia zarówno z rodzicem a dzieckiem, kiedy komunikat może przekraczać gwarancji POSIX na tym niskim poziomie pisze < = 512 bajtów wejdą strumień wyjściowy atomowo (zgodnie z Linuksem "man 7 pipe"). Ponadto, aby zachować chronologię z wieloma pisarzami, trzeba by przepłukać wyższy poziom Funkcje C lub C++ po każdym zapisie. Całkowicie bezpieczne jest jednak posiadanie wielu nagrywarek, jeśli wiadomo, że wiadomości mieszczą się w limicie i wykonywane są tylko operacje zapisu() niskiego poziomu.

+0

Ad 4. ​​POSIX gwarantuje, że krótkie zapisy (<= 512 bajtów) są gwarantowane wysyłane atomowo (bez przeplatania). Jednak nie pomaga to, gdy używane są wyjścia wysokiego poziomu, takie jak iostreams. – zch

+0

Znalazłem go w potoku strony podręcznika systemu Linux (7). Wyszukaj PIPE_BUF. Jeśli chodzi o POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html Opis pread mówi wprost, że odczyty nie są atomowe. – zch

+0

Ty, @zsh. Dodałem twoje punkty i dodałem kilka wyjaśnień i kilka wskazówek dotyczących średniego poziomu. – FauChristian

Powiązane problemy