O pano de fundo importante aqui é que stdout
é necessário que a linha seja armazenada em buffer pelo padrão como configuração padrão.
Isso faz com que \n
a descarga da saída.
Como o segundo exemplo não contém a nova linha, a saída não é liberada e, como fork()
copia todo o processo, também copia o estado do stdout
buffer.
Agora, essas fork()
chamadas no seu exemplo criam 8 processos no total - todos com uma cópia do estado do stdout
buffer.
Por definição, todos esses processos são chamados exit()
ao retornar main()
e exit()
chamadas fflush()
seguidos fclose()
em todos os fluxos de stdio ativos . Isso inclui stdout
e, como resultado, você vê o mesmo conteúdo oito vezes.
É uma boa prática chamar fflush()
todos os fluxos com saída pendente antes de chamar fork()
ou permitir que o filho bifurcado chame explicitamente _exit()
que só sai do processo sem liberar os fluxos de stdio.
Observe que a chamada exec()
não libera os stdio buffers, portanto, não há problema em se preocupar com os stdio buffers se você (após a chamada fork()
) ligar exec()
e (se isso falhar) ligar _exit()
.
BTW: Para entender que o buffer incorreto pode causar, eis um bug anterior no Linux que foi corrigido recentemente:
O padrão requer stderr
que não seja stderr
armazenado o buffer por padrão, mas o Linux ignorou isso e tornou a linha armazenada em buffer e (ainda pior) totalmente armazenada em buffer, caso o stderr fosse redirecionado através de um canal. Assim, os programas escritos para UNIX produziram coisas sem nova linha tarde demais no Linux.
Veja o comentário abaixo, parece estar corrigido agora.
Isto é o que eu faço para solucionar esse problema do 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);
Esse código não faz mal a outras plataformas, já que chamar fflush()
um fluxo que acabou de ser liberado é um noop.
./prog1 > prog1.out
) ou um pipe (./prog1 | cat
). Se prepare para ficar extremamente surpreso. :-)