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 \na 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 stdoutbuffer.
Agora, essas fork()chamadas no seu exemplo criam 8 processos no total - todos com uma cópia do estado do stdoutbuffer.
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 stdoute, 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 stderrque não seja stderrarmazenado 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. :-)