O arquivo usado pelo processo pai e filho

2
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>


int main( int argc, char *argv[] ){


    FILE *fptr;
    pid_t pid;

    fptr = fopen("Shared File.txt", "a");
    pid = fork();

    if( pid > 0 ){ // parent process

        int counter = 0;

        while( counter < 10 ){
            fprintf(fptr, "a");
            ++counter;
        }
        wait(NULL);
    }
    else{

        int counter = 0;

        while( counter < 5 ){
            fprintf(fptr, "b");
            ++counter;
        }
    }

    return 0;
}

Quando executo este código, o arquivo produzido pelo código contém esta mensagem: bbbbbaaaaaaaaaa

Sempre que executo este código, recebo a mesma mensagem. Por que os processos não são gravados no arquivo na ordem de embaralhamento?

Por que o sistema operacional tenta concluir o processo filho primeiro?

Minha expectativa sobre a mensagem é assim: baabbaaabaaabaa Não há transição contínua entre os processos.

    
por Goktug 26.05.2018 / 11:54

4 respostas

6

O agendamento entre pai e filho já foi discutido (pelo menos) em Quando os processos filhos são executados e Como a chamada do sistema fork realmente funciona .

Mas, nesse caso, há também a questão do buffer em stdio . Você está usando fprintf() para gravar em um arquivo normal. Por padrão, stdio armazena em buffers a saída para arquivos regulares até que dados suficientes sejam gravados, para economizar na sobrecarga da chamada do sistema. No x86 Linux, ele geralmente parece escrever em blocos de 4096 bytes, mas você não pode contar com isso, a menos que você configure o buffer manualmente (veja setbuf() e amigos).

Você pode ver isso com um comando como strace que mostra as chamadas do sistema que um programa faz.

Portanto, embora você não possa fazer previsões sobre qual processo é executado primeiro, neste caso, você pode prever que a s são gravados consecutivamente e b s também. Você só pode obter bbbbbaaaaaaaaaa ou aaaaaaaaaabbbbb , e qual você obtém depende do acaso.

    
por 26.05.2018 / 12:08
2

Concordo principalmente com @ikkachu, exceto

Qual deles você recebe bbbbbaaaaaaaaaa ou aaaaaaaaaabbbbb é previsível. O sistema operacional aguarda a conclusão do filho, devido a wait(NULL) e, em seguida, o pai sai. Como os buffers são liberados na saída, a criança começa a escrever primeiro.

No entanto, não confie nos buffers sendo previsíveis. Use flush explícito quando necessário.

    
por 27.05.2018 / 00:02
1

O usuário ilkkachu deu uma boa explicação de como o buffer afeta a saída. Minha resposta descreve o que aconteceria se você eliminasse o buffer, por exemplo substituindo as chamadas fprintf pelas chamadas para write . Nesse caso, você deve alternar estritamente a s e b s. Isso ocorre porque a chamada para write causa uma reprogramação: escrever em blocos de um processo, passar para o outro processo e assim por diante.

Vamos imaginar o que aconteceria se a chamada para escrever fosse não bloqueada. Então teríamos que considerar os cronogramas: você obteria execuções muito mais longas de a s e b s do que apenas um ou dois de cada vez, porque os processadores modernos são capazes de executar bilhões de instruções por segundo, mas a frequência de agendamento é tipicamente algo entre 100 Hz e 1000 Hz. Um processo seria capaz de executar até dezenas de milhões de instruções antes de ser preterido e o outro processo está programado para ser executado. Mesmo levando em conta a sobrecarga da chamada do sistema, isso daria tempo ao processo para imprimir strings muito longas de a s ou b s.

    
por 26.05.2018 / 19:52
1

ikkachu e Johan fizeram um ótimo trabalho explicando porque você observou o comportamento que você fez, então eu reescrevi um pouco seu programa para que cada processo liberasse o fluxo e dormisse por um segundo (então cada thread tem a chance de intercalar de forma diferente cada tempo), para que você possa ver o efeito de intercalação mais claramente.

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void) {
  FILE *fptr = stdout;
  pid_t pid;
  int counter = 0;

  pid = fork();

  if(pid > 0) { // parent process
    while(counter++ < 10) {
      fprintf(fptr, "aa");
      fflush(fptr);
      sleep(1);
    }
    wait(NULL);
    puts("");
  } else {
    while(counter++ < 5) {
      fprintf(fptr, "bbbb");
      fflush(fptr);
      sleep(1);
    }
  }
}

Se você executar isso várias vezes, obterá resultados diferentes de vez em quando:

~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbbbbbaaaaaaaaaaaa
~ $ ./thread
aabbbbaabbbbaabbbbaabbbbaabbbbaaaaaaaaaa
    
por 27.05.2018 / 03:46