#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 eu 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 gravam no arquivo em ordem aleatória?
Por que o sistema operacional tenta terminar o processo filho primeiro?
Minha expectativa sobre a mensagem é assim: baabbaaaaabaaabaa Não há transição contínua entre os processos.
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, neste caso, também há a questão do buffer dentro do
stdio
. Você está usandofprintf()
para gravar em um arquivo normal. Por padrão,stdio
armazena a saída em arquivos regulares até que dados suficientes sejam gravados, para economizar na sobrecarga de chamada do sistema. No Linux x86, geralmente parece gravar em blocos de 4096 bytes, mas você não pode contar com isso, a menos que defina o buffer manualmente (vejasetbuf()
e amigos).Você pode ver isso com um comando como
strace
o que mostra as chamadas do sistema que um programa faz.Portanto, embora você não possa fazer nenhuma previsão sobre qual processo será executado primeiro, nesse caso, você pode prever que os
a
s serão escritos consecutivamente e osb
s também. Você só pode obterbbbbbaaaaaaaaaa
ouaaaaaaaaaabbbbb
, e qual deles você obtém depende muito do acaso.Eu concordo principalmente com @ikkachu, exceto
Qual deles você obtém
bbbbbaaaaaaaaaa
ouaaaaaaaaaabbbbb
é previsível. O sistema operacional espera que o filho termine, por causa dewait(NULL)
, então 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 na previsibilidade dos buffers. Use descarga explícita quando necessário.
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
fprintf
chamadas por chamadas parawrite
. Nesse caso, você obteriaa
s eb
s estritamente alternados. Isso porque a chamada awrite
causa um reagendamento: escrever em blocos de um processo, dar a vez no outro processo, e assim por diante.Vamos imaginar o que aconteceria se a chamada para escrever não fosse bloqueada. Então teríamos que considerar escalas de tempo: você obteria execuções muito mais longas de
a
s eb
s do que apenas um ou dois por 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 sofrer preempção e agendar a execução do outro processo. Mesmo levando em consideração a sobrecarga da chamada do sistema, isso daria ao processo tempo para imprimir strings muito longas dea
s ou s consecutivosb
.ikkachu e Johan fizeram um ótimo trabalho explicando por que você observou o comportamento que você fez, então eu reescrevi ligeiramente seu programa para que cada processo libere o fluxo e durma por um segundo (para que cada thread tenha a chance de intercalar de maneira diferente a cada vez), para que você possa ver o efeito de intercalação com mais clareza.
Se você executar isso várias vezes, obterá resultados diferentes de vez em quando: