1
Obrigado

Algumas palavras de agradecimento nunca são demais.

O que faz um fork?

Um "fork" é uma característica dos sistemas Unix ou Linux para duplicar um processo. Veja, a seguir, como ele funciona.



Para explicar como funciona um fork, partiremos de um processo que chamaremos, carinhosamente, de "papai". Este processo vai simplesmente se duplicar e os dois processos (pai e filho) mostrarão a cada um os seu status (pai ou filho).

Execução do papai

A fim de identificar claramente as questões do fork (garfo), observe seus descritores de arquivos, ou melhor, um dos mais importantes: o do fluxo de saída (stdout), ou seja, a tela. Vamos também colocar uma pequena variável global que será usada posteriormente para indicar o estado do processo (pai ou filho).


Veja como ficará o processo inicial:

O PAPAI      

Descritores de arquivos
1: (Stdout) Posição = 0

Variáveis globais
const char* quemsoueu = NULL;

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork();
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}



A seta vermelha aponta para a próxima instrução a ser executada. Como ainda não foi executado nada, o usuário está no começo do main. Então, execute as duas primeiras instruções:

O PAPAI     

Descritores de arquivos
1: (Stdout) Posição = 0

Variáveis globais
const char* quemsoueu = "O PAPAI";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork();
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}


Pode-se ver, em vermelho, as instruções que foram executadas, assim como os dados que foram modificados pela última declaração.

O fork

A próxima instrução é a mais difícil de entender, então precisará ser executada.

O PAPAI    

Descritores de arquivos
1: (Stdout) Posição = 0

Variáveis globais
const char* quemsoueu = "O PAPAI";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 1000
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}


O FILHO

Descritores de arquivos
1: (Stdout) Posição = 0

Variáveis globais
const char* quemsoueu = "O PAPAI";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 0
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}



O pai chamou o garfo e se duplicou. Isto significa várias coisas:
  • Um novo processo foi criado: ele é considerado como o filho do processo que chamou o fork()
  • Este processo é uma cópia fiel do seu pai. Aliás, a próxima instrução a ser executada será a mesma para ambos: a condição "if".
  • A função fork() não retorna a mesma coisa para ambos os processos. Para o filho, ele retornará o 0. Para o pai, ele retornará o pid do filho (seu número de processo).
  • Esta duplicação envolve algumas coisas sobre as variáveis e os descritores de arquivos.

Controlar a execução do pai e do filho

O PAPAI   

Descritores de arquivos
1: (Stdout) Posição = 0

Variáveis globais
const char* quemsoueu = "O PAPAI";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 1000
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}


O FILHO

Descritores de arquivos
1: (Stdout) Posição = 0

Variáveis globais
const char* quemsoueu = "O PAPAI";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 0
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}


Ambos os processos acabaram de verificar a condição if. Já que, no pai, a variável pid será diferente de 0, ele continuará no else. Em compensação, o filho entrará no bloco do "if" pois, para ele, o pid é igual a 0.

Importante: Assim se controla a execução do pai e do filho: verificando o valor da variável pid, que é diferente para ambos.

As variáveis e os descritores de arquivos

O PAPAI  

Descritores de arquivos
1: (Stdout) Posição = 15

Variáveis globais
const char* quemsoueu = "O PAPAI";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 1000
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}


O FILHO

Descritores de arquivos
1: (Stdout) Posição = 15

Variáveis globais
const char* quemsoueu = "O filho";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 0
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}


Atenção, este ponto não pode ser negligenciado!
  • O filho mudou o valor da sua variável quemsoueu. Isso mudou o valor de sua própria variável quemsoueu, mas não a do pai. Sendo assim, as variáveis do pai e do filho são inteiramente distintas; mesmo que tenham o mesmo nome, não são as mesmas variáveis. Por outro lado, você notará que no momento do "fork", o filho tinha herdado os valores de todas as variáveis do seu pai.
  • O pai acabou de fazer um printf e, consequentemente, escreveu: "Eu sou o pai" no fluxo de saída padrão (stdout). Assim, após este registro, o ponteiro do arquivo stdout aumentou para 15 caracteres (o comprimento da frase exibida). Então, aconteceu o mesmo com o filho.


Na verdade, se, por uma lado, o pai e o filho têm variáveis distintas, por outro, os seus descritores de arquivos são os mesmos . Então, se um dos dois processos muda o seu ponteiro de posição em um arquivo, isso também será refletido no outro.

Atenção: isso só é válido para os descritores de arquivos herdados durante o fork. Se o pai ou o filho abrirem outros arquivos após o fork, esses descritores não serão compartilhados entre eles. Da mesma forma, se o filho fecha um descritor de arquivo herdado do pai, o descritor de arquivo do pai não será fechado (idem no sentido contrário).

Sincronização

O PAPAI 

Descritores de arquivos
1: (Stdout) Posição = 30

Variáveis globais
const char* quemsoueu = "O PAPAI";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 1000
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}


O FILHO

Descritores de arquivos
1: (Stdout) Posição = 30

Variáveis globais
const char* quemsoueu = "O filho";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 0
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}
  • Quanto ao Filho: um printf foi feito, desta vez para exibir "Eu sou o filho." O ponteiro do arquivo aumentou para 15 no filho, o que se reflete no pai.
  • Quanto ao Pai: o pai executou a função wait(). Esta função permite a sincronização entre o pai e todos os seus filhos. Isto significa que o pai vai parar de se executar (neste caso, diz-se que ele está dormindo) até que seu filho termine completamente.


O PAPAI

Descritores de arquivos
1: (Stdout) Posição = 30

Variáveis globais
const char* quemsoueu = "O PAPAI";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 1000
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}


O FILHO

Descritores de arquivos
1: (Stdout) Posição = 30

Variáveis globais
const char* quemsoueu = "O filho";

int main()
{
pid_t pid;
quemsoueu = "O papai";
pid = fork(); //pid = 0
if(pid = = 0){
quemsoueu = "O filho";
printf("Eu sou %s, quemsoueu);
}
else{
printf("Eu sou %s, quemsoueu);
wait(NULL);
}
return 0;
}



O filho acabou de executar a sua última instrução, então, agora, ele não existe mais. Durante esse tempo, o pai ainda estava esperando, mas logo ele irá acordar, já que o filho terminou. Finalmente, o pai também vai acabar.

Foto: ©Sai Kiran Anagani - Unsplash

Veja também

Este documento, intitulado 'O que faz um fork ()?', está disponível sob a licença Creative Commons. Você pode copiar e/ou modificar o conteúdo desta página com base nas condições estipuladas pela licença. Não se esqueça de creditar o CCM (br.ccm.net) ao utilizar este artigo.

0 Comentário