fork () verzweigt mehr als erwartet?

186

Betrachten Sie den folgenden Code:

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

Dieses Programm gibt 8 Punkte aus. Wie kann das möglich sein? Sollte es nicht stattdessen 6 Punkte geben?

Nikolay Kovalenko
quelle

Antworten:

245

Das fork()Primitive regt oft die Vorstellungskraft an. Bis Sie ein Gefühl dafür bekommen, sollten Sie auf Papier nachvollziehen, was jeder Vorgang ist, und die Anzahl der Prozesse berücksichtigen. Vergessen Sie nicht, dass fork () eine nahezu perfekte Kopie des aktuellen Prozesses erstellt. Der wichtigste Unterschied (für die meisten Zwecke) besteht darin, dass fork()der Rückgabewert zwischen Eltern und Kind unterschiedlich ist. (Da dieser Code den Rückgabewert ignoriert, macht er keinen Unterschied.)

Zunächst gibt es also einen Prozess. Dadurch wird ein zweiter Prozess erstellt, bei dem beide einen Punkt und eine Schleife drucken. Bei der zweiten Iteration erstellt jeder eine weitere Kopie, sodass vier Prozesse einen Punkt drucken und dann beenden. So können wir problemlos sechs Punkte berücksichtigen, wie Sie es erwarten.

Was jedoch printf()wirklich tut, ist die Ausgabe zu puffern. Der erste Punkt aus der Zeit, als es nur zwei Prozesse gab, wird beim Schreiben nicht angezeigt. Diese Punkte verbleiben im Puffer, der bei fork () dupliziert wird. Erst wenn der Prozess beendet ist, wird der gepufferte Punkt angezeigt. Vier Prozesse drucken einen gepufferten Punkt, und der neue ergibt 8 Punkte.

Wenn Sie dieses Verhalten vermeiden möchten, rufen Sie fflush(stdout);danach an printf().

Wallyk
quelle
12
Danke, ich wusste nicht, dass der Puffer mit fork () dupliziert wird. Es erklärt so ein seltsames Verhalten.
Nikolay Kovalenko
1
Sollte das nicht 10 Punkte geben, nicht 8? Da die 4 Kinder der zweiten Generation den gepufferten Punkt erben, ihren eigenen hinzufügen und beim Beenden leeren, würden sie insgesamt 8 Punkte drucken, aber dann würden die 2 Prozesse der ersten Generation immer noch jeweils einen Punkt gepuffert und die beim Beenden leeren. Geben von insgesamt 10.
Psusi
12
@psusi Einer der Prozesse der zweiten Generation ist ein Prozess der ersten Generation. fork()erstellt nicht 2, sondern beendet, sondern nur 1 weiteren Prozess.
Izkata
70

Sie haben nicht festgeschriebene Puffer in den Ausgabestreams . stdout ist zeilengepuffert und der Puffer wird zusammen mit dem Rest des Prozesses repliziert. Wenn das Programm beendet wird, wird der nicht festgeschriebene Puffer zweimal geschrieben (einmal für jeden Prozess). Beide verwenden

printf("a\n");

und

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

Zeigen Sie das Problem nicht.

In Ihrem ersten Beispiel erstellen Sie vier Prozesse, deren Ausgabestream-Puffer jeweils zwei Punkte enthält. Wenn jeder Stream beendet wird, wird sein Puffer geleert und acht Punkte generiert.

Thiton
quelle
2

wenn i = 0

Prozess_1: Gepufferter Text = 1 Punkt

Process_2 (erstellt von Process_1): Gepufferter Text = 1 Punkt

wenn i = 1

Process_3 (erstellt von Process_1): Erbt 1 gepufferten Punkt von Process_1 und druckt 1 Punkt selbst. Insgesamt druckt Process_3 2 Punkte.

Process_4 (erstellt von Process_2): Erbt 1 gepufferten Punkt von Process_2 und druckt 1 Punkt selbst. Insgesamt druckt Process_4 2 Punkte.

Prozess_1: Druckt 2 Punkte (Ein gepufferter Punkt bei i = 0 und ein weiterer Punkt bei i = 1)

Prozess_2: Druckt 2 Punkte (Ein gepufferter Punkt bei i = 0 und ein weiterer Punkt bei i = 1)

Endgültige Ausgabe: 8 Punkte. :) :)

Tauseef
quelle