web-dev-qa-db-fra.com

Pourquoi ce programme affiche-t-il "Forked!" 4 fois?

Pourquoi ce programme imprime-t-il "fourchu!" 4 fois?

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

int main(void) {

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

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

La première fork() renvoie une valeur non nulle dans le processus appelant (appelez-le p0) et 0 dans l'enfant (appelez-le p1).

Dans p1, le court-circuit de && Est pris et le processus appelle printf et se termine. Dans p0, le processus doit évaluer le reste de l'expression. Ensuite, il appelle à nouveau fork(), créant ainsi un nouveau processus enfant (p2).

Dans p0 fork() renvoie une valeur non nulle et le court-circuit de || Est pris, donc le processus appelle printf et se termine.

En p2, fork() retourne 0 donc le reste de || doit être évaluée, qui est la dernière fork(); cela conduit à la création d'un enfant pour p2 (appelez-le p3).

P2 exécute ensuite printf et se termine.

P3 exécute alors printf et se termine.

4 printfs sont ensuite exécutés.

84

L'un vient de main() et les trois autres de chaque fork().

Notez que les trois forks() vont être exécutées. Vous voudrez peut-être jeter un oeil à la ref :

VALEUR DE RETOUR

En cas de réussite, fork () renverra 0 au processus enfant et renverra l'ID de processus du processus enfant au processus parent. Les deux processus doivent continuer à s'exécuter à partir de la fonction fork (). Sinon, -1 doit être renvoyé au processus parent, aucun processus enfant ne doit être créé et errno doit être défini pour indiquer l'erreur.

Notez que l'ID de processus ne peut pas être nul, comme indiqué ici .


Que se passe-t-il vraiment?

On a:

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

Ainsi, la première fork() renverra au parent son identifiant de processus non nul, tandis qu'elle renverra 0 au processus enfant. Cela signifie que la première fourchette de l'expression logique sera évaluée à true dans le processus parent, tandis que dans le processus enfant, elle sera évaluée à false et, en raison de évaluation de court-circuit , elle n'appellera pas le reste deux fork() s.

Donc, maintenant nous savons que nous allons obtenir au moins deux impressions (une de main et une de la 1ère fork()).

Maintenant, la 2e fork() du processus parent va être exécutée, elle le fait et elle renvoie une valeur non nulle au processus parent et une zéro dans le processus enfant.

Alors maintenant, le parent ne poursuivra pas l'exécution jusqu'à la dernière fork() (en raison d'un court-circuit), tandis que le processus enfant exécutera la dernière fourchette, car le premier opérande de || Est 0.

Cela signifie donc que nous aurons deux impressions supplémentaires.

En conséquence, nous obtenons quatre impressions au total.


Court-circuit

Ici, court-circuitage signifie essentiellement que si le premier opérande de && est nul, alors le ou les autres opérandes ne sont pas évalués. Sur la même logique, si un opérande de || est égal à 1, les autres opérandes n'ont pas besoin d'être évalués. Cela se produit car les autres opérandes ne peuvent pas modifier le résultat de l'expression logique, ils n'ont donc pas besoin d'être exécutés, ce qui nous fait gagner du temps.

Voir l'exemple ci-dessous.


Processus

N'oubliez pas qu'un processus parent crée des processus descendants qui à leur tour créent d'autres processus et ainsi de suite. Cela conduit à une hiérarchie des processus (ou à un arbre pourrait-on dire).

Ayant cela à l'esprit, il vaut la peine de jeter un œil à cette réponse problème similaire , ainsi qu'à this .


Image descriptive

J'ai aussi fait ce chiffre qui peut aider, je suppose. J'ai supposé que la fork() renvoyée par le pid était 3, 4 et 5 pour chaque appel.

fork nodes Notez que certaines fork() ont un X rouge au-dessus, ce qui signifie qu'elles ne sont pas exécutées en raison de l'évaluation de court-circuit de l'expression logique.

Les fork() s en haut ne vont pas être exécutées, car le premier opérande de l'opérateur && Est 0, donc l'expression entière se traduira par 0, donc aucune essence dans l'exécution du reste de l'opérande ou des opérandes de &&.

La fork() en bas ne sera pas exécutée, car c'est le deuxième opérande d'un ||, Où son premier opérande est un nombre non nul, donc le résultat de l'expression est déjà évalué à vrai, quel que soit le deuxième opérande.

Et dans l'image suivante, vous pouvez voir la hiérarchie des processus: Process hierarchy basé sur la figure précédente.


Exemple de court-circuit

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

Sortie:

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

Pour tous les downvoters, cela vient d'une question fusionnée mais différente. Blâme-le. Merci.

Vous pouvez décomposer le problème en trois lignes, la première et la dernière lignes doublent simplement le nombre de processus.

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

Les opérateurs court-circuitent, voici donc ce que vous obtenez:

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

Il s'agit donc en tout de 4 * 5 = 20 processus d'impression chacun d'une ligne.

Remarque: Si pour une raison quelconque fork () échoue (par exemple, vous avez une certaine limite sur le nombre de processus), il renvoie -1 et vous pouvez alors obtenir des résultats différents.

14
Karoly Horvath

Exécution de fork() && (fork() || fork()), que se passe-t-il

Chaque fork donne 2 processus avec respectivement les valeurs pid (parent) et 0 (enfant)

Première fourche:

  • la valeur de retour parent est pid non null => exécute la && (fork() || fork())
    • la deuxième valeur du parent fork est pid non null arrête l'exécution de la partie || => print forked
    • deuxième valeur enfant fork = 0 => exécute la || fork()
      • troisième parent fork imprime forked
      • troisième fourche enfant imprime forked
  • la valeur de retour enfant est 0 arrêtez d'exécuter la && partie => imprime forked

Total: 4 forked

9
Serge Ballesta

J'aime toutes les réponses qui ont déjà été soumises. Peut-être que si vous ajoutiez quelques variables supplémentaires à votre instruction printf, il serait plus facile pour vous de voir ce qui se passe.

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

int main(){

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

Sur ma machine, il a produit cette sortie:

forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1
8
Bradley Slavik

Ce code:

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

obtient 20 processus pour lui-même et 20 fois Printf ira.

Et pour

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

printf ira un total de 5 fois.

5
Mayank Dixit