Tengo un programa C simple para iniciar el proceso de tiempo (preferiría no publicar el código completo ya que es una tarea escolar activa). Mi función principal se ve así:
int main(void) { int i; for (i = 0; i < 5; i++) { printf("%lf\n", sample_time()); } exit(0); } sample_time() es una función que mide el tiempo que se tarda en bifurcar un nuevo proceso y devuelve el resultado en segundos como un double . La parte de sample_time() que se bifurca:
double sample_time() { // timing stuff if (!fork()) exit(0); // immediately close new process // timing stuff return a_sample_time; } Como era de esperar, ejecutar el programa, times , en la terminal genera 5 números así:
$ ./times 0.000085 0.000075 0.000079 0.000071 0.000078Sin embargo, intentar redirigir esto a un archivo (oa cualquier otro lugar) en una terminal Unix produce resultados inesperados.
Por ejemplo, ./times > times.out crea un archivo con quince números. Además, ./times | wc -l genera 15 , lo que confirma el resultado anterior. Corriendo ./times | cat , vuelvo a ver quince números, más de cinco de los cuales son distintos .
¿Alguien sabe qué diablos puede causar algo como esto? Estoy fuera de las ideas.
./times != ./times | cat Wat.
Dato 1 : cuando la salida estándar está conectada a un TTY, tiene un búfer de línea. Cuando está conectado a un archivo o a una canalización, está completamente almacenado en búfer. Esto significa que solo se vacía cada 8 KB, digamos , en lugar de cada línea.
Hecho 2 : los procesos bifurcados tienen copias duplicadas de los datos en memoria. Esto incluye los búferes de salida de stdio si los datos aún no se han vaciado.
Hecho 3 : llamar a exit() hace que los búferes de salida de stdio se vacíen antes de que finalice el programa.
Cuando su programa se imprime en la terminal, su salida se almacena en línea. Cada llamada a printf() que termina con \n se imprime inmediatamente. Esto significa que cada línea se imprime y el búfer de salida en memoria se vacía antes de que se ejecute fork() .
Resultado : 5 líneas de salida.
Cuando libc ve que stdout no está conectado a un TTY, cambia a una estrategia de almacenamiento en búfer completo más eficiente. Esto hace que la salida se almacene en búfer hasta que se hayan acumulado 4 KB. Eso significa que la salida de printf() s se guarda en la memoria y las llamadas a write() se posponen.
if (!fork()) exit(0); Después de la bifurcación, el proceso secundario tiene una copia de la salida almacenada en búfer. La llamada exit() luego hace que ese búfer se vacíe. Sin embargo, esto no afecta el proceso principal. Su salida aún está almacenada en búfer .
Luego, cuando se imprime la segunda línea de salida, tiene dos líneas almacenadas en búfer. El siguiente proceso secundario bifurca, sale e imprime esas dos líneas. El padre conserva sus dos líneas de salida, y así sucesivamente.
Resultado: el niño procesa la impresión de 0, 1, 2, 3 y 4 líneas de salida. El programa principal imprime 5 cuando finalmente sale y vacía su salida. 0 + 1 + 2 + 3 + 4 + 5 = 15. ¡15 líneas de salida en lugar de 5!
Llame _Exit() en lugar de exit() . La función _Exit() es como exit() , pero no llama a ninguna función registrada con atexit() . Esta sería mi solución preferida.
Establecer explícitamente stdout para que se búfer de línea: setvbuf(stdout, NULL, _IOLBF, 0);
Llame fflush(stdout) después de cada printf .