Ważnym tłem jest to, że stdout
zgodnie z ustawieniami domyślnymi wymagane jest buforowanie linii przez standard.
Powoduje to \n
opróżnienie wyjścia.
Ponieważ drugi przykład nie zawiera nowej linii, dane wyjściowe nie są opróżniane, a ponieważ fork()
kopiuje cały proces, kopiuje również stan stdout
bufora.
Teraz te fork()
wywołania w twoim przykładzie tworzą łącznie 8 procesów - wszystkie z kopią stanu stdout
bufora.
Z definicji wszystkie te procesy wywołują exit()
po powrocie, main()
a następnie exit()
wywołania na wszystkich aktywnych strumieniach stdio . Obejmuje to, w wyniku czego widzisz tę samą treść osiem razy.fflush()
fclose()
stdout
Dobrą praktyką jest wywoływanie fflush()
wszystkich strumieni z oczekującymi fork()
danymi wyjściowymi przed wywołaniem lub zezwolenie na rozwidlone wywołanie potomne, _exit()
które wychodzi z procesu bez opróżniania strumieni stdio.
Zauważ, że dzwonienie exec()
nie opróżnia buforów stdio, więc nie martw się o bufory stdio, jeśli (po wywołaniu fork()
) zadzwonisz exec()
i (jeśli to się nie powiedzie) _exit()
.
BTW: Aby zrozumieć, że może to spowodować nieprawidłowe buforowanie, oto poprzedni błąd w systemie Linux, który został niedawno naprawiony:
Standard wymaga stderr
domyślnie stderr
buforowania bufora, ale Linux zignorował to i sprawił, że linia została buforowana i (jeszcze gorzej) całkowicie buforowana na wypadek, gdyby stderr został przekierowany przez potok. Więc programy napisane dla UNIXa wypisały rzeczy bez Linii zbyt późno w Linuksie.
Patrz komentarz poniżej, wydaje się, że jest to teraz naprawione.
Oto co robię, aby obejść ten problem z Linuksem:
/*
* Linux comes with a broken libc that makes "stderr" buffered even
* though POSIX requires "stderr" to be never "fully buffered".
* As a result, we would get garbled output once our fork()d child
* calls exit(). We work around the Linux bug by calling fflush()
* before fork()ing.
*/
fflush(stderr);
Ten kod nie wyrządza szkody na innych platformach, ponieważ wywołanie fflush()
strumienia, który został właśnie opróżniony, jest noop.
./prog1 > prog1.out
) lub potoku (./prog1 | cat
). Przygotuj się na zawrót głowy. :-)