fork () branches plus que prévu?

186

Considérez le morceau de code suivant:

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

Ce programme produit 8 points. Comment est-ce possible? Ne devrait-il pas y avoir 6 points à la place?

Nikolay Kovalenko
la source
14
ideone.com/B9HXL
Antonio Pérez

Réponses:

245

Le fork()primitif étire souvent l'imagination. Jusqu'à ce que vous en ayez une idée, vous devez tracer sur papier ce qu'est chaque opération et rendre compte du nombre de processus. N'oubliez pas que fork () crée une copie presque parfaite du processus actuel. La différence la plus significative (dans la plupart des cas) est que fork()la valeur de retour de s diffère entre parent et enfant. (Puisque ce code ignore la valeur de retour, cela ne fait aucune différence.)

Donc, au début, il y a un processus. Cela crée un deuxième processus, qui imprime tous deux un point et une boucle. Lors de leur deuxième itération, chacun crée une autre copie, il y a donc quatre processus qui impriment un point, puis se terminent. Nous pouvons donc facilement rendre compte de six points, comme vous vous en doutez.

Cependant, ce printf()qui fait vraiment est tamponner sa sortie. Ainsi, le premier point à partir de quand il n'y avait que deux processus n'apparaît pas lors de l'écriture. Ces points restent dans le tampon - qui est dupliqué à fork (). Ce n'est que lorsque le processus est sur le point de se terminer que le point tamponné apparaît. Quatre processus imprimant un point tamponné, plus le nouveau donne 8 points.

Si vous vouliez éviter ce comportement, appelez fflush(stdout);après printf().

marcher
la source
12
Merci, je ne savais pas que le tampon se dupliquait avec fork (). Cela explique un comportement si étrange.
Nikolay Kovalenko
1
Cela ne devrait-il pas donner 10 points, pas 8? Étant donné que les 4 enfants de deuxième génération héritent du point tamponné, ajoutent le leur, puis rincent à la sortie, ils imprimeraient un total de 8 points, mais alors les 2 processus de première génération auraient toujours un point chacun tamponné, et videraient ceux à la sortie, soit un total de 10.
psusi
12
@psusi L'un des processus de deuxième génération est un processus de première génération. fork()ne crée pas 2 puis quitte, il crée seulement 1 processus supplémentaire.
Izkata
70

Vous avez des tampons non validés dans les flux de sortie . stdout est tamponné en ligne et le tampon est répliqué avec le reste du processus. Lorsque le programme se termine, le tampon non validé est écrit deux fois (une fois pour chaque processus). Les deux utilisant

printf("a\n");

et

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

ne présentez pas le problème.

Dans votre premier exemple, vous créez quatre processus qui ont chacun deux points dans leur tampon de flux de sortie. Lorsque chaque flux se termine, il vide sa mémoire tampon, générant huit points.

thiton
la source
2

quand i = 0

Process_1: texte tamponné = 1 point

Process_2 (créé par Process_1): texte tamponné = 1 point

quand i = 1

Process_3 (créé par Process_1): hérite de 1 point en mémoire tampon de Process_1 et imprime 1 point par lui-même. Au total, Process_3 imprime 2 points.

Process_4 (créé par Process_2): hérite de 1 point tamponné de Process_2 et imprime 1 point par lui-même. Au total, Process_4 imprime 2 points.

Process_1: Imprime 2 points (un point en mémoire tampon lorsque i = 0 et un autre point lorsque i = 1)

Process_2: imprime 2 points (un point en mémoire tampon lorsque i = 0 et un autre point lorsque i = 1)

Sortie finale: 8 points. :)

Tauseef
la source