oddziały fork () więcej niż oczekiwano?

186

Rozważ następujący fragment kodu:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    int i;
    for(i = 0; i < 2; i++)
    {
        fork();
        printf(".");
    }
    return 0;
}

Ten program generuje 8 kropek. Jak to możliwe? Czy zamiast tego nie powinno być 6 kropek?

Nikolay Kovalenko
źródło
14
ideone.com/B9HXL
Antonio Pérez

Odpowiedzi:

245

fork()Prymitywne często ciągnie wyobraźnię. Dopóki nie poczujesz tego, powinieneś prześledzić na papierze, czym jest każda operacja, i uwzględnić liczbę procesów. Nie zapominaj, że fork () tworzy niemal idealną kopię bieżącego procesu. Najbardziej znacząca różnica (dla większości celów) polega na tym fork(), że zwracana wartość różni się między rodzicem a dzieckiem. (Ponieważ ten kod ignoruje zwracaną wartość, nie ma znaczenia).

Tak więc na początku jest jeden proces. To tworzy drugi proces, z których oba drukują kropkę i pętlę. Podczas drugiej iteracji każdy tworzy kolejną kopię, więc cztery procesy drukują kropkę, a następnie kończą. Możemy więc z łatwością uwzględnić sześć kropek, tak jak się spodziewasz.

Jednak tak printf()naprawdę buforuje dane wyjściowe. Tak więc pierwsza kropka z okresu, w którym były tylko dwa procesy, nie pojawia się po napisaniu. Te kropki pozostają w buforze - który jest duplikowany w fork (). Buforowana kropka pojawia się dopiero po zakończeniu procesu. Cztery procesy drukują buforowaną kropkę, a nowy daje 8 kropek.

Jeśli chcesz uniknąć takiego zachowania, zadzwoń fflush(stdout);po printf().

wallyk
źródło
12
Dzięki, nie wiedziałem, że bufor powiela się za pomocą fork (). To wyjaśnia takie dziwne zachowanie.
Nikolay Kovalenko
1
Czy to nie powinno dać 10 kropek, a nie 8? Ponieważ dzieci potomne 4 drugiej generacji dziedziczą buforowaną kropkę, dodają własną, a następnie opróżniają przy wyjściu, wydrukowałyby łącznie 8 kropek, ale wtedy procesy 2 pierwszej generacji nadal miałyby po jednej zbuforowanej kropce i opróżniały te przy wyjściu, dając w sumie 10.
psusi
12
@psusi Jednym z procesów drugiej generacji jest proces pierwszej generacji. fork()nie tworzy 2, a następnie kończy, tworzy tylko 1 dodatkowy proces.
Izkata
70

Masz niezatwierdzone bufory w strumieniach wyjściowych . stdout jest buforowany liniowo, a bufor jest replikowany wraz z resztą procesu. Po zakończeniu programu nieprzydzielony bufor jest zapisywany dwukrotnie (raz dla każdego procesu). Oba używają

printf("a\n");

i

printf("a "); fflush(stdout);

nie pokazuj problemu.

W pierwszym przykładzie utworzysz cztery procesy, które mają dwie kropki w buforze strumienia wyjściowego. Po zakończeniu każdego strumienia opróżnia bufor, generując osiem kropek.

Thiton
źródło
2

gdy i = 0

Proces_1: Buforowany tekst = 1 kropka

Process_2 (utworzony przez Process_1): buforowany tekst = 1 kropka

gdy i = 1

Process_3 (utworzony przez Process_1): Dziedziczy 1 buforowaną kropkę z Process_1 i drukuje 1 kropkę samodzielnie. W sumie Process_3 drukuje 2 kropki.

Process_4 (utworzony przez Process_2): Dziedziczy 1 buforowaną kropkę z Process_2 i drukuje 1 kropkę samodzielnie. W sumie Process_4 drukuje 2 kropki.

Proces_1: Drukuje 2 kropki (jedna buforowana kropka, gdy i = 0, a druga kropka, gdy i = 1)

Proces_2: Drukuje 2 kropki (jedna buforowana kropka, gdy i = 0, a druga kropka, gdy i = 1)

Końcowy wynik: 8 kropek. :)

Tauseef
źródło