A duplicação do buffer de saída na chamada de sistema fork() do Linux

Este artigo relata uma situação inusitada que ocorreu durante a execução de um programa de teste da chamada de sistema fork() do Unix e do Linux, esclarecendo detalhes sobre o funcionamento desta chamada de sistema no que diz respeito a buffers.

[ Hits: 29.855 ]

Por: Roland Teodorowitsch em 29/03/2009


Conclusão



Este artigo apresentou uma situação de uso da chamada de sistema fork() do Unix que pode gerar resultados diferentes do esperado. Basicamente o que ocorre é que a chamada fork() duplica um processo, incluindo o buffer de saída, ou seja, incluindo tudo o que o processo-pai (criador) mandou imprimir com printf(), fprintf(stdout, ...) ou chamadas afins, e que ainda não foi descarregado do buffer de saída (stdout).

O comportamento esperado parte do princípio de o que é impresso aparece no vídeo imediatamente, o que nem sempre é verdade. Como a entrada e saída do Unix passa por um buffer, o conteúdo do buffer só é esvaziado no final de uma linha, quando o buffer está cheio ou quando força-se este esvaziamento (através da chamada fflush()). Desta forma, quando o buffer não é esvaziado, podem ocorrer comportamentos não esperados em chamadas como fork(), que duplicam o processo, incluindo o seus buffers.

Tal comportamento foi verificado no Linux, versões de núcleo 2.6. Acredita-se que as versões anteriores de Linux tenham o mesmo comportamento. É provável que outras versões de Unix também tenham o mesmo comportamento, mas isto não foi comprovado na prática.

A forma como o programa se manifestou nos fornece algumas interpretações interessantes para a história. A primeira delas é de que se não procurarmos experimentar situações novas, manteremos nosso conhecimento limitado à situação atual. É importante vivenciar situações novas.

Os alunos que escreveram o programa ao seu modo mostraram também que, às vezes, seguir um caminho diferente do que a maioria segue pode levar a caminhos diferentes e interessantes.

Ao mesmo tempo, à primeira vista, o problema mostrou-se sem explicação: pequenas diferenças no código-fonte gerando resultados diferentes. No entanto, uma análise mais detalhada, apresentou uma explicação plausível e comprovável para a situação.

Página anterior    

Páginas do artigo
   1. Introdução
   2. Um novo desafio
   3. A resposta
   4. Conclusão
Outros artigos deste autor
Nenhum artigo encontrado.
Leitura recomendada

Programação com números inteiros gigantes

Compilando Templates C++

GNA: um Coprocessador para Aceleração Neural

Como funcionam os alocadores de memória do STD C?

Dynamic libraries com libtool

  
Comentários
[1] Comentário enviado por f_Candido em 29/03/2009 - 09:19h

Muito Legal. Uma pequena alteração... Faz toda a diferença.

Abraços

[2] Comentário enviado por pedroarthur.jedi em 30/03/2009 - 09:28h

Ficou muito bom o post!
E a didática do "vamos fazer pra entender" se mostrou bastante eficiente!

Parabéns!

[3] Comentário enviado por elgio em 30/03/2009 - 11:50h

Muito bom, muito bom mesmo.

ao analisar o código sem executar:

for (i=0; i<2; ++i) {
. . . fork();
. . . printf("%d",i);
}

Esperaria que ele imprimisse 001111

A saber:

primeiro 0: pai imprime 0
segundo 0: primeiro filho imprime 0
primeiro 1: pai imprime 1 (e termina)
segundo 1: primeiro filho imprime 1 (e termina)
demais 1's: terceiro e quarto filhos, que são criados com i=1; imprimem 1.

Imprimir em outra ordem já deve ser efeito do buffer e/ou do escalonador do Linux, certo?

Entendo que colocando fflush para forçar a saída do buffer:

for (i=0; i<2; ++i) {
. . . fork();
. . . printf("%d",i);
. . . fflush(stdout);
}

pode-se ter sequências DIFERENTES de impressão. Exemplo: com um core, é provável que o pai termine toda a sua execução antes de passar a CPU para o filho. Já em um CORE 2, pode pai e filho imprimirem "ao mesmo tempo" (disputando a exclusividade da tela?) e qualquer combinação pode ser apresentada. Correto?

[4] Comentário enviado por pedroarthur.jedi em 30/03/2009 - 11:59h

Pela minha experiência com programação concorrente, diria que não há saída preditível. Tudo vai depender do escalonador, da carga do sistema, da quantidade de (núcleos|processadores) e outros fatores. Claro que haverá saídas mais prováveis que outras. Mas como dizem os grandes mestres, (Tanenbaum, Silberchartz) temos que estar preparado para o pior...

[5] Comentário enviado por Douglas_Martins em 31/03/2009 - 10:32h

Eu estava nessa aula e ficamos mesmo intrigados com o que tinha ocorrido.
Valeu pela explicação Roland...


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts