Warum gibt dieses Programm “fork!” 4 Mal?

Lesezeit: 8 Minuten

Benutzeravatar von Rawan Lezzeik
Rawan Lezzeik

Warum gibt dieses Programm „forked!“ aus? 4 Mal?

#include <stdio.h>
#include <unistd.h>

int main(void) {

  fork() && (fork() || fork());

  printf("forked!\n");
  return 0;
}

Benutzeravatar von gsamaras
Gsamaras

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

Beachten Sie, dass alle drei forks() werden hingerichtet. Vielleicht möchten Sie einen Blick auf die werfen Ref:

RÜCKGABEWERT

Nach erfolgreichem Abschluss fork() soll 0 an den untergeordneten Prozess zurückgeben und soll die Prozess-ID des untergeordneten Prozesses an den übergeordneten Prozess zurückgeben. Beide Prozesse sollen weiterhin von der Funktion fork() ausgeführt werden. Andernfalls wird -1 an den übergeordneten Prozess zurückgegeben, kein untergeordneter Prozess erstellt und errno zur Anzeige des Fehlers gesetzt.

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


Was passiert also wirklich?

Wir haben:

fork() && (fork() || fork());

Also das erste fork() gibt dem Elternprozess seine Nicht-Null-Prozess-ID zurück, während er 0 an den Kindprozess zurückgibt. Das 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 nicht aufruft fork()s.

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

Nun, der 2 fork() im übergeordneten Prozess ausgeführt werden soll, tut dies und gibt einen Wert ungleich null an den übergeordneten Prozess und einen Wert von null im untergeordneten Prozess zurück.

Jetzt wird der Elternteil die Ausführung nicht bis zum letzten fortsetzen fork() (aufgrund von Kurzschlüssen), während der untergeordnete Prozess die letzte Verzweigung seit dem ersten Operanden von ausführt || ist 0.

Das bedeutet also, dass wir zwei weitere Drucke bekommen werden.

Als Ergebnis erhalten wir insgesamt vier Drucke.


Kurzschluss

Hier, Kurzschluss bedeutet im Grunde, dass wenn der erste Operand von && Null ist, der/die andere(n) Operand(en) nicht ausgewertet wird/werden. Nach der gleichen Logik, wenn ein Operand eines || 1 ist, müssen die restlichen Operanden nicht ausgewertet werden. Dies geschieht, weil die restlichen Operanden das Ergebnis des logischen Ausdrucks nicht ändern können, also nicht ausgeführt werden müssen, wodurch wir Zeit sparen.

Siehe Beispiel unten.


Verfahren

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

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


Beschreibendes Bild

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

Gabelknoten
Beachten Sie, dass einige fork()s haben ein rotes X darüber, was bedeutet, dass sie wegen der kurzschließenden Auswertung des logischen Ausdrucks nicht ausgeführt werden.

Das fork()s ganz oben werden nicht ausgeführt, da der erste Operand des Operators && ist 0, daher wird der gesamte Ausdruck zu 0 führen, also ist es nicht wesentlich, den Rest der Operanden von auszuführen &&.

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

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


Beispiel für Kurzschluss

#include <stdio.h>

int main(void) {

  if(printf("A printf() results in logic true\n"))
    ;//empty body

  if(0 && printf("Short circuiting will not let me execute\n"))
    ;
  else if(0 || printf("I have to be executed\n"))
    ;
  else if(1 || printf("No need for me to get executed\n"))
    ;
  else
  printf("The answer wasn't nonsense after all!\n");

  return 0;
}

Ausgabe:

A printf() results in logic true
I have to be executed

Benutzeravatar von Jean-Baptiste Yunès
Jean-Baptiste Yunes

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

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

In p0 fork() gibt einen Wert ungleich Null zurück, und der Kurzschluss für || genommen wird, also ruft der Prozess auf printf und beendet.

In p2, fork() gibt 0 zurück, also den Rest von || muss ausgewertet werden, das ist das letzte fork(); das führt zur Erstellung eines untergeordneten Elements für p2 (nennen Sie es p3).

P2 wird dann ausgeführt printf und beendet.

P3 wird dann ausgeführt printf und beendet.

4 printfs werden dann ausgeführt.

  • könnten Sie bitte erklären “Kurzschluss für && wird genommen”?

    – Rawan Lezzeik

    3. November 2014 um 15:04 Uhr

  • @rona-altico, überprüfe den Link zum Kurzschluss in meiner Antwort. Es bedeutet im Grunde, dass wenn der erste Operand von && Null ist, dann werden die anderen Operanden nicht ausgewertet. Auf der gleichen Logik, wenn ein Operand von a || 1 ist, dann müssen die restlichen Operanden nicht ausgewertet werden. Dies geschieht, weil die Rest-Operanden das Ergebnis des logischen Ausdrucks nicht ändern können und daher nicht ausgeführt werden müssen, wodurch wir Zeit sparen. Besser jetzt Rona? Nette Frage übrigens, ich kann nicht verstehen, warum die Ablehnungen. Sie haben meine +1 bekommen.

    – Gsamaras

    3. November 2014 um 15:09 Uhr


  • @rona-altico: Es fällt mir schwer zu verstehen, warum Sie verwenden möchten fork(), aber nicht einmal wissen, was Kurzschluss ist. War das eine Frage in der Schule?

    – Sebastian Mach

    4. November 2014 um 11:55 Uhr

  • @G.Samaras: Ich schätze, es wird abgelehnt, weil es eines von vielen Duplikaten ist, und zeigt keine Mühe, es zu googeln, bevor Sie hier fragen.

    – chr

    4. November 2014 um 12:45 Uhr

  • @G.Samaras: Sie können nicht sehen, warum die Ablehnungen? Das ist ein abscheulich Frage. Es gibt ein exaktes Duplikat und er hat sich nicht die Mühe gemacht zu erklären, welche unterschiedliche Ausgabe er erwartet hat. Warum dies 41 positive Stimmen hat, ist mir völlig schleierhaft; normalerweise erreicht so etwas schnell -3 oder -4.

    – Leichtigkeitsrennen im Orbit

    6. November 2014 um 17:24 Uhr


Benutzeravatar von Karoly Horvath
Karl Horvath

Für alle Downvoter stammt dies aus einer zusammengeführten, aber anderen 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.

fork() && fork() || fork();

Die Operatoren schließen kurz, also erhalten Sie Folgendes:

       fork()
      /      \
    0/        \>0
 || fork()     && fork()
     /\            /   \
    /  \         0/     \>0
   *    *     || fork()  *
                /   \
               *     *

Das sind also insgesamt 4 * 5 = 20 Prozesse, die jeweils eine Zeile drucken.

Hinweis: Wenn fork() aus irgendeinem Grund fehlschlägt (z. B. wenn die Anzahl der Prozesse begrenzt ist), gibt es -1 zurück und Sie können dann andere Ergebnisse erhalten.

  • Danke @yi_H für deine Antwort und eine Sache, wenn nur diese Zeile in unserem Code vorhanden wäre, dann wäre die Ausgabe 5-mal gegabelt??

    – Amit Singh Tomar

    17. August 2011 um 11:29 Uhr

  • Und auch fork1() || Gabel2() && Gabel3(); Diese Anweisung führt zu 16 Prozessen.

    – Amit Singh Tomar

    17. August 2011 um 11:34 Uhr

  • Hi @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

    25. Oktober 2011 um 8:58 Uhr

  • Ohne Klammern analysieren Sie den logischen Baum möglicherweise falsch. Führt der falsche (0) Pfad von der ersten fork() nicht dazu, dass der gesamte Ausdruck am && kurzgeschlossen wird? ich denken 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 kurzschließen kann (innerhalb von Klammern). Es ist dasselbe wie fork() && (fork() || fork()) Dies würde dann die 4 (nicht 5) Prozesse aus dieser Zeile allein und 16 insgesamt erklären. Es mag in C++ oder C# anders sein, aber diese Frage war in C.

    – Rob Parker

    4. November 2014 um 0:03 Uhr

  • Dies beantwortet die Frage, die verwendet fork() && fork() || fork();während die Frage hier verwendet fork() && (fork() || fork());. Es gab eine Zusammenführung, wie hier besprochen: “meta.stackoverflow.com/questions/281729/…”. Möglicherweise möchten Sie Ihre Antwort bearbeiten und zukünftige Leser benachrichtigen.

    – Gsamaras

    5. Januar 2015 um 18:14 Uhr


Ausführen fork() && (fork() || fork())was geschieht

Jeder fork gibt 2 Prozesse mit den jeweiligen Werten pid (Eltern) und 0 (Kind)

Erste Gabel:

  • übergeordneter Rückgabewert ist pid not null => führt die aus && (fork() || fork())
    • Der zweite Fork-Elternwert ist pid not null stoppt die Ausführung von || Teil => drucken forked
    • second fork child value = 0 => führt die aus || fork()
      • Elternabdrücke der dritten Gabel forked
      • drittes Gabelkind druckt forked
  • untergeordneter Rückgabewert ist 0 Ausführung des &&-Teils stoppen => druckt forked

Insgesamt: 4 forked

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

#include<stdio.h>
#include<unistd.h>

int main(){

   long child = fork() && (fork() || fork());
   printf("forked! PID=%ld Child=%ld\n", getpid(), child);
   return 0;
}

Auf meiner Maschine erzeugte es diese Ausgabe:

forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1

  • Was ist mit den Werten, die von jedem Aufruf von fork() zurückgegeben werden? Wie wäre es mit: long f1,f2,f3; (f1 = fork()) && ((f2 = fork()) || (f3 = fork())); und dann die PID und die drei Einzelwerte drucken.

    – Rob Parker

    4. November 2014 um 0:10 Uhr

Benutzeravatar von Peter Mortensen
Peter Mortensen

Dieser Code:

fork();
fork() && fork() || fork();
fork();

bekommt 20 Prozesse für sich und 20 mal wird Printf gehen.

Und für

fork() && fork() || fork();

printf wird insgesamt 5 Mal ausgeführt.

  • Was ist mit den Werten, die von jedem Aufruf von fork() zurückgegeben werden? Wie wäre es mit: long f1,f2,f3; (f1 = fork()) && ((f2 = fork()) || (f3 = fork())); und dann die PID und die drei Einzelwerte drucken.

    – Rob Parker

    4. November 2014 um 0:10 Uhr

1417690cookie-checkWarum gibt dieses Programm “fork!” 4 Mal?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy