2011-01-20 16 views
8

Estaba buscando un código simple en el tenedor, y decidí probarlo por mi cuenta. Lo compilé y luego lo ejecuté desde dentro de Emacs, y obtuve una salida diferente a la salida producida al ejecutarlo en Bash.¿Por qué la salida de mi programa de horquilla es diferente cuando canalizo su salida?

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

int main() { 
    if (fork() != 0) { 
    printf("%d: X\n", getpid()); 
    } 

    if (fork() != 0) { 
    printf("%d: Y\n", getpid()); 
    } 

    printf("%d: Z\n", getpid()); 
} 

He compilado con gcc, y luego corrió a.out desde dentro de Emacs, así como las tuberías que cat y grep ., y me encargo.

2055: X
2055: Y
2055: Z
2055: X
2058: Z
2057: Y
2057: Z
2059: Z

Esto no es correcto Ejecutarlo sólo de Bash recibo (que yo esperaba)

2084: X
2084: Y
2084: Z
2085: Y
2085: Z
2087: Z
2086: Z

edición - se perdió algunas nuevas líneas

¿Qué está pasando?

+0

¿Por qué estás canalizando los resultados a través de cualquier cosa (especialmente "gato" que realmente no hará nada en absoluto)? - Oh, espera, son las tuberías las que introducen la rareza ... mmm ... – Pointy

+0

Puedo reproducir esto, así que no son rayos cósmicos ni nada. Intrigante. Tenga en cuenta que falta un PID (2056) en la salida anterior; ese es probablemente el PID de 'cat'. – Thomas

+0

Lo ejecuté varias veces, la mayoría de las cuales no tenían PID faltantes. – Squidly

Respuesta

11

El orden en que los diferentes procesos escriben su salida es completamente impredecible. Entonces, la única sorpresa es que a veces la declaración de impresión "X" a veces ocurre dos veces.

Creo que esto se debe a que a veces en el segundo fork(), una línea de salida que incluye "X" se encuentra en un búfer de salida, necesitando enjuagarse. Entonces, ambos procesos finalmente lo imprimen. Como getpid() ya se llamó y se convirtió en la cadena, mostrarán el mismo pid.

Pude reproducir varias líneas "X", pero si agrego fflush(stdout); justo antes del segundo fork(), siempre veo una sola línea "X" y siempre un total de 7 líneas.

8

Creo que sé lo que está pasando. El almacenamiento en memoria intermedia stdio será diferente cuando la salida sea un tty frente a cuando se trata de una tubería o un archivo. Los procesos secundarios heredan los almacenamientos intermedios principales. Cuando están enrojecidos, puedes obtener doble salida.

Si agrega

fflush(stdout); 

justo después de cada llamada printf(), verá lo que quiero decir.

Lo interesante es que es diferente cuando la salida estándar es un dispositivo tty. Puede ser que la biblioteca sepa lo que eso significa, y se vacía después de cada salto de línea, o algo así.

+0

¡De hecho! Poner un 'fflush (stdout);' después de cada 'printf' lo resuelve. El contenido del búfer de salida se copia en el momento de la horquilla, por lo que la misma salida almacenada se imprime dos veces cuando se vacía el búfer. – Thomas

+0

Esto es correcto. 'stdout' es buffer de línea por defecto cuando se escribe en un terminal interactivo (por lo tanto, se vacía después de cada' \ n'), pero se almacena completamente en el búfer cuando se escribe en un conducto. Un único 'fflush (stdout);' inmediatamente antes de cada 'fork()' también funcionará. – caf

6

Entonces, ¿imagino que se está preguntando por qué está recibiendo más de una "X"?

Esto se debe a que la salida del búfer se vacía dos veces.

Cuando canaliza la salida de un programa, la biblioteca stdio reconoce que su salida no es un terminal, y cambia al almacenamiento en búfer de bloque en lugar del búfer de línea. En consecuencia, todavía no hay ningún resultado cuando el proceso se bifurca y, por lo tanto, ahora el padre y el hijo tienen salida pendiente.

3

Si ha utilizado stdout en todos antes de que se bifurcan, se necesidad llamada fflush(stdout) antes fork() (y lo mismo para cualquier otro tipo de salida FILE s que usa). De lo contrario, resulta en comportamiento indefinido. El efecto que está viendo proviene de stdout siendo con buffer de línea cuando está conectado a un terminal, pero totalmente amortiguado cuando está conectado a una tubería. Esto no es obligatorio, pero lo recomiendan los estándares (POSIX).

Cuestiones relacionadas