web-dev-qa-db-fra.com

Visuellement ce qui arrive à fork () dans une boucle For

J'ai essayé de comprendre fork() comportement. Cette fois dans un for-loop. Observez le code suivant:

#include <stdio.h>

void main()
{
   int i;

   for (i=0;i<3;i++)
   {
      fork();

      // This printf statement is for debugging purposes
      // getppid(): gets the parent process-id
      // getpid(): get child process-id

      printf("[%d] [%d] i=%d\n", getppid(), getpid(), i);
   }

   printf("[%d] [%d] hi\n", getppid(), getpid());
}

Voici la sortie:

[6909][6936] i=0
[6909][6936] i=1
[6936][6938] i=1
[6909][6936] i=2
[6909][6936] hi
[6936][6938] i=2
[6936][6938] hi
[6938][6940] i=2
[6938][6940] hi
[1][6937] i=0
[1][6939] i=2
[1][6939] hi
[1][6937] i=1
[6937][6941] i=1
[1][6937] i=2
[1][6937] hi
[6937][6941] i=2
[6937][6941] hi
[6937][6942] i=2
[6937][6942] hi
[1][6943] i=2
[1][6943] hi

Je suis une personne très visuelle et la seule façon pour moi de vraiment comprendre les choses est de faire des diagrammes. Mon instructeur a dit qu'il y aurait 8 énoncés salut. J'ai écrit et couru le code, et en effet il y avait 8 déclarations salut. Mais je ne l’ai vraiment pas compris. J'ai donc dessiné le schéma suivant:

enter image description here

Le schéma a été mis à jour pour refléter les commentaires:)

Observations:

  1. Le processus parent (principal) doit itérer la boucle 3 fois. Alors printf s'appelle
  2. À chaque itération du parent for-loop, un fork () est appelé
  3. Après chaque appel fork (), i est incrémenté et chaque enfant commence une boucle for à partir de i avant de l'incrémenter.
  4. À la fin de chaque boucle for, "hi" est imprimé

Voici mes questions:

  • Mon diagramme est-il correct?
  • Pourquoi y a-t-il deux instances de i=0 dans la sortie?
  • Quelle valeur de i est reportée sur chaque enfant après le fork ()? Si la même valeur de i est reportée, à quel moment le "forking" s'arrête-t-il?
  • Est-ce toujours le cas que 2^n - 1 serait un moyen de compter le nombre d’enfants qui sont fourchés? Alors, ici n=3, ce qui signifie 2^3 - 1 = 8 - 1 = 7 enfants, qui est correct?
62
lucidgold

Voici comment le comprendre, en commençant par la boucle for.

  1. La boucle commence dans le parent, i == 0

  2. Parent fork() s, création de l'enfant 1.

  3. Vous avez maintenant deux processus. Les deux affichent i=0.

  4. La boucle redémarre dans les deux processus, maintenant i == 1.

  5. Parent et enfant 1 fork(), créant les enfants 2 et 3.

  6. Vous avez maintenant quatre processus. Tous les quatre affichent i=1.

  7. La boucle redémarre dans les quatre processus, maintenant i == 2.

  8. Le parent et les enfants 1 à 3 tous fork(), créant les enfants de 4 à 7 ans.

  9. Vous avez maintenant huit processus. Tous les huit affichent i=2.

  10. La boucle redémarre dans les huit processus, maintenant i == 3.

  11. La boucle se termine dans les huit processus, car i < 3 N'est plus vrai.

  12. Les huit processus affichent tous hi.

  13. Les huit processus se terminent tous.

Ainsi, vous obtenez 0 Imprimé deux fois, 1 Imprimé quatre fois, 2 Imprimé 8 fois et hi imprimé 8 fois.

38
Paul Griffiths
  1. Oui c'est correct. (voir ci-dessous)
  2. Non, i++ Est exécuté après l'appel de fork, car c'est ainsi que fonctionne la boucle for.
  3. Si tout se passe bien, oui. Cependant, rappelez-vous que fork peut échouer.

Une petite explication sur le second:

for (i = 0;i < 3; i++)
{
   fork();
}

est similaire à:

i = 0;
while (i < 3)
{
    fork();
    i++;
}

Donc, i dans les processus forkés (parent et enfant) est la valeur avant incrément. Cependant, l'incrément est exécuté immédiatement après fork(), donc, à mon avis, le diagramme pourrait être considéré comme correct.

12
Yu Hao

Pour répondre à vos questions une à une:

Mon diagramme est-il correct?

Oui, essentiellement. C'est aussi un très beau diagramme.

C'est-à-dire qu'il est correct si vous interprétez les étiquettes i=0 Etc. comme faisant référence à des itérations de boucle complète. Ce que le diagramme ne montre pas montre cependant, c'est qu'après chaque fork(), la partie de l'itération de la boucle en cours après l'appel fork() est également exécutée par le processus fils forké.

Pourquoi y a-t-il deux instances de i=0 Dans la sortie?

Comme vous avez le printf() après le fork(), il est donc exécuté à la fois par le processus parent et par le processus enfant qui vient d'être créé. Si vous déplacez la printf() avant la fork(), elle ne sera exécutée que par le parent (car le processus enfant n'existe pas encore).

Quelle valeur de i est reportée sur chaque enfant après la fork()? Si la même valeur de i est reportée, à quel moment le "forking" s'arrête-t-il?

La valeur de i n'est pas modifiée par fork(); le processus enfant voit donc la même valeur que son parent.

La chose à retenir à propos de fork() est qu'il s'appelle une fois, mais qu'il retourne deux fois - une fois dans le processus parent et une fois dans le processus enfant récemment cloné.

Pour un exemple plus simple, considérons le code suivant:

printf("This will be printed once.\n");
fork();
printf("This will be printed twice.\n");
fork();
printf("This will be printed four times.\n");
fork();
printf("This will be printed eight times.\n");

Le processus enfant créé par fork() est un clone (presque) exact de son parent. Ainsi, de son propre point de vue, il "se souvient" en étant son parent, héritant de tout l'état du processus parent (y compris toutes les variables). valeurs, la pile d'appels et l'instruction en cours d'exécution). La seule différence immédiate (autre que les métadonnées système telles que l'ID de processus renvoyé par getpid()) est la valeur renvoyée par fork(), qui sera égale à zéro dans le processus enfant mais non nulle ( en fait, l’ID du processus enfant) dans le parent.

Est-il toujours vrai que 2^n - 1 Serait un moyen de compter le nombre d'enfants fourchus? Donc, ici n=3, Qui signifie 2^3 - 1 = 8 - 1 = 7 Enfants, lequel est correct?

Chaque processus qui exécute une fork() se transforme en deux processus (sauf dans des conditions d'erreur inhabituelles, où fork() peut échouer). Si le parent et l'enfant continuent à exécuter le même code (c'est-à-dire qu'ils ne vérifient pas la valeur de retour de fork(), ni leur propre ID de processus, et qu'ils se connectent à des chemins de code différents en fonction de celui-ci), chaque branche suivante doublera le nombre de processus. Donc, oui, après trois fourchettes, vous obtiendrez 2³ = 8 processus au total.

3
Ilmari Karonen