fork () se ramifica más de lo esperado?

186

Considere la siguiente pieza de código:

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

Este programa genera 8 puntos. ¿Cómo puede ser eso posible? ¿No debería haber 6 puntos en su lugar?

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

Respuestas:

245

Lo fork()primitivo a menudo estira la imaginación. Hasta que lo sienta, debe rastrear en papel cuál es cada operación y tener en cuenta la cantidad de procesos. No olvide que fork () crea una copia casi perfecta del proceso actual. La diferencia más significativa (para la mayoría de los propósitos) es que fork()el valor de retorno difiere entre padre e hijo. (Dado que este código ignora el valor de retorno, no hace ninguna diferencia).

Entonces, al principio, hay un proceso. Eso crea un segundo proceso, que imprime un punto y un bucle. En su segunda iteración, cada uno crea otra copia, por lo que hay cuatro procesos que imprimen un punto y luego salen. Por lo tanto, podemos dar cuenta de seis puntos, como es de esperar.

Sin embargo, lo que printf()realmente hace es amortiguar su salida. Entonces, el primer punto de cuando solo había dos procesos no aparece cuando se escribe. Esos puntos permanecen en el búfer, que se duplica en fork (). No es hasta que el proceso está a punto de salir que aparece el punto almacenado. Cuatro procesos que imprimen un punto almacenado, más el nuevo da 8 puntos.

Si desea evitar ese comportamiento, llame fflush(stdout);después printf().

wallyk
fuente
12
Gracias, no sabía que el búfer se duplica con fork (). Explica un comportamiento tan extraño.
Nikolay Kovalenko
1
¿No debería eso dar 10 puntos, no 8? Dado que los 4 niños de la segunda generación heredan el punto almacenado, agregan los suyos y luego enjuagan al salir, imprimirían un total de 8 puntos, pero luego los 2 procesos de primera generación aún tendrían un punto cada uno almacenado, y enjuagan los que están al salir, dando un total de 10.
psusi
12
@psusi Uno de los procesos de segunda generación es un proceso de primera generación. fork()no crea 2 y luego sale, solo crea 1 proceso más.
Izkata
70

Tiene memorias intermedias no confirmadas en las secuencias de salida . stdout tiene buffer de línea y el buffer se replica junto con el resto del proceso. Cuando finaliza el programa, el búfer no confirmado se escribe dos veces (una para cada proceso). Ambos usando

printf("a\n");

y

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

No exhibas el problema.

En su primer ejemplo, crea cuatro procesos que tienen cada dos puntos en su búfer de flujo de salida. Cuando cada secuencia termina, vacía su búfer, generando ocho puntos.

thiton
fuente
2

cuando i = 0

Proceso_1: texto en búfer = 1 punto

Proceso_2 (creado por Proceso_1): texto en búfer = 1 punto

cuando i = 1

Process_3 (creado por Process_1): Hereda 1 punto almacenado del Process_1 e imprime 1 punto por sí mismo. En total, Process_3 imprime 2 puntos.

Proceso_4 (creado por Proceso_2): Hereda 1 punto almacenado del Proceso_2 e imprime 1 punto por sí mismo. En total, Process_4 imprime 2 puntos.

Proceso_1: Imprime 2 puntos (un punto almacenado cuando i = 0 y otro punto cuando i = 1)

Proceso_2: Imprime 2 puntos (un punto almacenado cuando i = 0 y otro punto cuando i = 1)

Salida final: 8 puntos. :)

Tauseef
fuente