AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 447898
Accepted
lmaololrofl
lmaololrofl
Asked: 2018-06-05 19:05:15 +0800 CST2018-06-05 19:05:15 +0800 CST 2018-06-05 19:05:15 +0800 CST

Por que um programa com fork() às vezes imprime sua saída várias vezes?

  • 772

No Programa 1 Hello worldé impresso apenas uma vez, mas quando eu removo \ne executo (Programa 2), a saída é impressa 8 vezes. Alguém pode me explicar o significado de \naqui e como isso afeta o fork()?

Programa 1

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("hello world...\n");
    fork();
    fork();
    fork();
}

Saída 1:

hello world... 

Programa 2

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("hello world...");
    fork();
    fork();
    fork();
}

Saída 2:

hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...
c fork
  • 4 4 respostas
  • 24466 Views

4 respostas

  • Voted
  1. Best Answer
    Kusalananda
    2018-06-05T20:24:54+08:002018-06-05T20:24:54+08:00

    Ao enviar para a saída padrão usando a printf()função da biblioteca C, a saída geralmente é armazenada em buffer. O buffer não é liberado até que você produza uma nova linha, chame fflush(stdout)ou saia do programa (mas não através da chamada _exit()). O fluxo de saída padrão é, por padrão, com buffer de linha dessa maneira quando conectado a um TTY.

    Quando você bifurca o processo no "Programa 2", os processos filho herdam todas as partes do processo pai, incluindo o buffer de saída não liberado. Isso copia efetivamente o buffer não liberado para cada processo filho.

    Quando o processo termina, os buffers são liberados. Você inicia um total geral de oito processos (incluindo o processo original) e o buffer não liberado será liberado no término de cada processo individual.

    É oito porque em cada um fork()você tem o dobro de processos que tinha antes do fork()(já que eles são incondicionais), e você tem três desses (2 3 = 8).

    • 105
  2. edc65
    2018-06-05T22:42:29+08:002018-06-05T22:42:29+08:00

    Não afeta o garfo de forma alguma.

    No primeiro caso, você acaba com 8 processos sem nada para escrever, porque o buffer de saída já foi esvaziado (devido ao \n).

    No segundo caso você ainda tem 8 processos, cada um com um buffer contendo "Hello world..." e o buffer é escrito no final do processo.

    • 19
  3. Honza Zidek
    2018-06-05T22:37:52+08:002018-06-05T22:37:52+08:00

    @Kusalananda explicou por que a saída é repetida . Se você está curioso por que a saída é repetida 8 vezes e não apenas 4 vezes (o programa base + 3 forks):

    int main()
    {
        printf("hello world...");
        fork(); // here it creates a copy of itself --> 2 instances
        fork(); // each of the 2 instances creates another copy of itself --> 4 instances
        fork(); // each of the 4 instances creates another copy of itself --> 8 instances
    }
    
    • 12
  4. schily
    2018-06-06T01:10:51+08:002018-06-06T01:10:51+08:00

    O contexto importante aqui é que stdouté necessário ter buffer de linha pelo padrão como configuração padrão.

    Isso faz \ncom que a limpe a saída.

    Como o segundo exemplo não contém a nova linha, a saída não é liberada e, ao fork()copiar todo o processo, também copia o estado do stdoutbuffer.

    Agora, essas fork()chamadas em seu exemplo criam 8 processos no total - todos eles com uma cópia do estado do stdoutbuffer.

    Por definição, todos esses processos chamam exit()ao retornar de main()e exit()chamadas fflush()seguidas por em todos os fluxos stdiofclose() ativos . Isso inclui e, como resultado, você vê o mesmo conteúdo oito vezes.stdout

    É 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 apenas sai do processo sem liberar os fluxos stdio.

    Observe que a chamada exec()não libera os buffers stdio, portanto, não há problema em não se preocupar com os buffers stdio se você (após chamar fork()) chamar exec()e (se isso falhar) chamar _exit().

    BTW: Para entender que o buffer errado pode causar, aqui está um antigo bug no Linux que foi corrigido recentemente:

    O padrão requer stderrser unbuffered por padrão, mas o Linux ignorou isso e fez stderrbuffer de linha e (pior ainda) totalmente bufferizado caso o stderr fosse redirecionado através de um pipe. Portanto, 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 contornar este 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 prejudica em outras plataformas, pois chamar fflush()um fluxo que acabou de ser liberado é um noop.

    • 5

relate perguntas

  • Por que os descritores de arquivo são compartilhados entre processos bifurcados?

  • Leia /proc/pid/maps usando read()

  • Quando o heap é usado para alocação dinâmica de memória?

  • O que exatamente o GNU faz dep faz?

  • Otimização de programas baseados em OpenCV sistema operacional linux incorporado [fechado]

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    ssh Não é possível negociar: "nenhuma cifra correspondente encontrada", está rejeitando o cbc

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    Como descarregar o módulo do kernel 'nvidia-drm'?

    • 13 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add retorna com: "Erro ao conectar ao agente: nenhum arquivo ou diretório" 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve