Lo sfondo importante qui è che stdout
è richiesto il buffer di linea dallo standard come impostazione predefinita.
Ciò provoca \n
a svuotare l'output.
Poiché il secondo esempio non contiene la nuova riga, l'output non viene svuotato e, poiché fork()
copia l'intero processo, copia anche lo stato del stdout
buffer.
Ora, queste fork()
chiamate nel tuo esempio creano 8 processi in totale, tutti con una copia dello stato del stdout
buffer.
Per definizione, tutti questi processi chiamano exit()
al ritorno da main()
e exit()
chiamate fflush()
seguite da fclose()
tutti i flussi stdio attivi . Ciò include stdout
e, di conseguenza, viene visualizzato lo stesso contenuto otto volte.
È buona norma chiamare fflush()
tutti i flussi con output in sospeso prima di chiamare fork()
o lasciare che il bambino biforcato chiami esplicitamente _exit()
che esce dal processo solo senza svuotare i flussi stdio.
Nota che la chiamata exec()
non scarica i buffer dello stdio, quindi è OK non preoccuparsi dei buffer dello stdio se tu (dopo aver chiamato fork()
) chiami exec()
e (se ciò fallisce) chiama _exit()
.
A proposito: per capire che può causare un buffering errato, ecco un ex bug in Linux che è stato corretto di recente:
Lo standard richiede stderr
di non essere bufferizzato per impostazione predefinita, ma Linux lo ha ignorato e ha reso il stderr
buffer di linea e (anche peggio) completamente bufferizzato nel caso in cui stderr fosse reindirizzato attraverso una pipe. Quindi i programmi scritti per UNIX hanno prodotto cose senza newline troppo tardi su Linux.
Vedi il commento qui sotto, sembra essere stato risolto ora.
Questo è quello che faccio per aggirare questo problema con Linux:
/*
* Linux comes with a broken libc that makes "stderr" buffered even
* though POSIX requires "stderr" to be never "fully buffered".
* As a result, we would get garbled output once our fork()d child
* calls exit(). We work around the Linux bug by calling fflush()
* before fork()ing.
*/
fflush(stderr);
Questo codice non danneggia le altre piattaforme poiché la chiamata fflush()
su uno stream appena scaricato è un noop.
./prog1 > prog1.out
) o in una pipe (./prog1 | cat
). Preparati a farti saltare la testa. :-)