Warum druckt dieses Programm "gegabelt!" 4 Mal?

76

Warum druckt dieses Programm "gegabelt!" 4 Mal?

Rawan Lezzeik
quelle

Antworten:

86

Der erste fork()gibt einen Wert ungleich Null im aufrufenden Prozess zurück (nennen Sie es p0) und 0 im untergeordneten Prozess (nennen Sie es p1).

In p1 wird der Kurzschluss für &&genommen und der Prozess wird aufgerufen printfund beendet. In p0 muss der Prozess den Rest des Ausdrucks auswerten. Dann wird fork()erneut aufgerufen, wodurch ein neuer untergeordneter Prozess erstellt wird (p2).

In p0 wird fork()ein Wert ungleich Null zurückgegeben, und der Kurzschluss für ||wird genommen, sodass der Prozess aufgerufen printfund beendet wird.

In p2 wird fork()0 zurückgegeben, also der Rest von || muss ausgewertet werden, welches das letzte ist fork(); das führt zur Erstellung eines Kindes für p2 (nenne es p3).

P2 wird dann ausgeführt printfund beendet.

P3 wird dann ausgeführt printfund beendet.

printfDann werden 4 s ausgeführt.

Jean-Baptiste Yunès
quelle
3
Könnten Sie bitte erklären, "Kurzschluss für && ist genommen"?
Rawan Lezzeik
8
@ rona-altico, überprüfe den Link über Kurzschluss in meiner Antwort. Dies bedeutet im Grunde, dass wenn der erste Operand von &&Null ist, die anderen Operanden nicht ausgewertet werden. Wenn ein Operand von a nach der gleichen Logik ||1 ist, müssen die restlichen Operanden nicht ausgewertet werden. Dies liegt daran, dass die restlichen Operanden das Ergebnis des logischen Ausdrucks nicht ändern können und daher nicht ausgeführt werden müssen. Dadurch sparen wir Zeit. Besser jetzt rona? Gute Frage übrigens, ich kann nicht verstehen, warum die Abstimmungen. Du hast meine +1.
Gsamaras
3
@ rona-altico: Es fällt mir schwer zu verstehen, warum Sie verwenden möchten fork(), aber ich weiß nicht einmal, was Kurzschluss ist. War das eine Frage in der Schule?
Sebastian Mach
2
@ G.Samaras: Ich denke, es ist herabgestimmt, weil es eines von vielen Duplikaten ist und keine Mühe zeigt, es zu googeln, bevor man hier fragt.
Chrk
4
@ G.Samaras: Du kannst nicht sehen warum die Downvotes? Das ist eine schreckliche Frage. Es gibt ein genaues Duplikat und er hat sich nicht die Mühe gemacht zu erklären, welche unterschiedlichen Ausgaben er erwartet hatte. Warum dies 41 positive Stimmen hat, ist mir völlig unverständlich. Normalerweise erreicht so etwas schnell -3 oder -4.
Leichtigkeitsrennen im Orbit
210

Der eine kommt von main()und die anderen drei von jedem fork().

Beachten Sie, dass alle drei forks()ausgeführt werden. Vielleicht möchten Sie einen Blick auf die Referenz werfen :

RÜCKGABEWERT

Nach erfolgreichem Abschluss gibt fork () 0 an den untergeordneten Prozess zurück und gibt die Prozess-ID des untergeordneten Prozesses an den übergeordneten Prozess zurück . Beide Prozesse müssen weiterhin über die Funktion fork () ausgeführt werden. Andernfalls wird -1 an den übergeordneten Prozess zurückgegeben, es wird kein untergeordneter Prozess erstellt, und errno wird festgelegt, um den Fehler anzuzeigen.

Beachten Sie, dass die Prozess-ID nicht Null sein kann, wie hier angegeben .


Was passiert also wirklich?

Wir haben:

Der erste fork()gibt also seine Prozess-ID ungleich Null an den übergeordneten Prozess zurück, während er 0 an den untergeordneten Prozess zurückgibt. Dies bedeutet, dass die erste Verzweigung des logischen Ausdrucks im übergeordneten Prozess als wahr ausgewertet wird, während sie im untergeordneten Prozess als falsch ausgewertet wird und aufgrund der Kurzschlussauswertung die verbleibenden zwei fork()s nicht aufruft .

Jetzt wissen wir also, dass wir mindestens zwei Drucke erhalten werden (einen vom Haupt- und einen vom 1. fork()).

Jetzt wird die zweite fork()im übergeordneten Prozess ausgeführt, und es wird ein Wert ungleich Null an den übergeordneten Prozess und eine Null im untergeordneten Prozess zurückgegeben.

Daher setzt das übergeordnete Element die Ausführung nicht bis zum letzten fort fork()(aufgrund eines Kurzschlusses), während der untergeordnete Prozess die letzte Verzweigung ausführt, da der erste Operand von ||0 ist.

Das bedeutet also, dass wir zwei weitere Abzüge erhalten.

Als Ergebnis erhalten wir insgesamt vier Abzüge.


Kurzschluss

Hier Kurzschlüsse im Grunde bedeutet , dass , wenn der erste Operand von && Null ist , dann wird der andere Operand (en) ist / sind nicht ausgewertet. Nach der gleichen Logik, wenn ein Operand von a || ist 1, dann müssen die restlichen Operanden nicht ausgewertet werden. Dies geschieht, weil der Rest der Operanden das Ergebnis des logischen Ausdrucks nicht ändern kann und sie daher nicht ausgeführt werden müssen, sodass wir Zeit sparen.

Siehe Beispiel unten.


Prozess

Denken Sie daran, dass ein übergeordneter Prozess Nachkommenprozesse erstellt, die wiederum andere Prozesse usw. erstellen. Dies führt zu einer Hierarchie von Prozessen (oder einem Baum, den man sagen könnte).

Vor diesem Hintergrund lohnt es sich, sich dieses ähnliche Problem sowie diese Antwort anzuschauen .


Beschreibendes Bild

Ich habe auch diese Figur gemacht, die helfen kann, denke ich. Ich nahm an, dass die zurückgegebenen PIDs fork()3, 4 und 5 für jeden Anruf sind.

Gabelknoten Beachten Sie, dass einige fork()s ein rotes X über sich haben, was bedeutet, dass sie aufgrund der Kurzschlussauswertung des logischen Ausdrucks nicht ausgeführt werden.

Die fork()s oben werden nicht ausgeführt, da der erste Operand des Operators &&0 ist. Daher führt der gesamte Ausdruck zu 0, sodass die Ausführung des Restes der Operanden von &&.

Das fork()untere wird nicht ausgeführt, da es der zweite Operand von a ist ||, wobei sein erster Operand eine Zahl ungleich Null ist. Daher wird das Ergebnis des Ausdrucks bereits als wahr ausgewertet, unabhängig davon, was der zweite Operand ist.

Und im nächsten Bild sehen Sie die Hierarchie der Prozesse: Prozesshierarchie basierend auf der vorherigen Abbildung.


Beispiel für einen Kurzschluss

Ausgabe:

gsamaras
quelle
14

Für alle Downvoter ist dies eine zusammengeführte, aber andere Frage. Schuld SO. Vielen Dank.

Sie können das Problem in drei Zeilen zerlegen. Die erste und die letzte Zeile verdoppeln einfach die Anzahl der Prozesse.

Die Bediener sind kurzgeschlossen, daher erhalten Sie Folgendes:

Das sind also insgesamt 4 * 5 = 20 Prozesse, bei denen jeweils eine Zeile gedruckt wird.

Hinweis: Wenn fork () aus irgendeinem Grund fehlschlägt (z. B. wenn Sie die Anzahl der Prozesse begrenzt haben), wird -1 zurückgegeben, und Sie können unterschiedliche Ergebnisse erhalten.

Karoly Horvath
quelle
Vielen Dank an @yi_H für Ihre Antwort und eine Sache, wenn nur diese Zeile in unserem Code vorhanden ist, dann wäre die Ausgabe 5-mal gegabelt?
Amit Singh Tomar
Und auch fork1 () || fork2 () && fork3 (); Diese Aussage führt zu 16 Prozessen.
Amit Singh Tomar
Hallo @yi_H bin nur verwirrt, wie "fork1 () || fork2 () && fork3 ()" insgesamt 16 ergibt. Ich meine, kann ich ein Diagramm wie dieses haben, das Sie oben erwähnt haben
Amit Singh Tomar
Ohne Klammern analysieren Sie den logischen Baum möglicherweise falsch. Verursacht der falsche (0) Pfad von der ersten Gabel () nicht, dass der gesamte Ausdruck am && kurzgeschlossen wird? Ich denke, die Assoziativitätspriorität von && und || in C sind von rechts nach links, so dass eine einfache Auswertung von links nach rechts den Rest des Unterausdrucks (in Klammern) kurzschließen kann. Es ist dasselbe wie fork () && (fork () || fork ()) Dies würde dann die 4 (nicht 5) Prozesse allein aus dieser Zeile und insgesamt 16 erklären. Es mag in C ++ oder C # anders sein, aber diese Frage war in C.
Rob Parker
3
Dies beantwortet die verwendete Frage fork() && fork() || fork();, während die hier verwendete Frage verwendet wird fork() && (fork() || fork());. Es gab eine Zusammenführung, wie hier beschrieben: " meta.stackoverflow.com/questions/281729/… ". Möglicherweise möchten Sie Ihre Antwort bearbeiten und zukünftige Leser benachrichtigen.
Gsamaras
9

Ausführen fork() && (fork() || fork()), was passiert

Jeder forkgibt 2 Prozesse mit den Werten pid (Eltern) und 0 (Kind).

Erste Gabel:

  • Der übergeordnete Rückgabewert ist pid not null => führt das aus && (fork() || fork())
    • Der übergeordnete Wert der zweiten Gabel ist pid not null und beendet die Ausführung des ||Teils => printforked
    • zweiter untergeordneter Gabelwert = 0 => führt die aus || fork()
      • Drucke der dritten Gabel übergeordnet forked
      • dritte Gabel Kind druckt forked
  • Der untergeordnete Rückgabewert ist 0. Beenden Sie die Ausführung der && part => -Drucke forked

Gesamt: 4 forked

Serge Ballesta
quelle
8

Ich mag alle Antworten, die bereits eingereicht wurden. Wenn Sie Ihrer printf-Anweisung ein paar weitere Variablen hinzufügen, können Sie möglicherweise leichter sehen, was passiert.

Auf meiner Maschine hat es diese Ausgabe erzeugt:

Bradley Slavik
quelle
5
Was ist mit den Werten, die bei jedem Aufruf von fork () zurückgegeben werden? Wie wäre es: long f1,f2,f3; (f1 = fork()) && ((f2 = fork()) || (f3 = fork()));und drucken Sie dann die PID und die drei Einzelwerte aus.
Rob Parker
5

Dieser Code:

Erhält 20 Prozesse für sich und 20 Mal wird Printf gehen.

Und für

printf wird insgesamt 5 mal gehen.

Mayank Dixit
quelle
2
Dies beantwortet die verwendete Frage fork() && fork() || fork();, während die hier verwendete Frage verwendet wird fork() && (fork() || fork());. Es gab eine Zusammenführung, wie hier beschrieben: " meta.stackoverflow.com/questions/281729/… ". Möglicherweise möchten Sie Ihre Antwort bearbeiten und zukünftige Leser benachrichtigen.
Gsamaras