Dado o programa fork-print.c a seguir:
fork-print.c
/*
Criação de processos em UNIX, com impressão de valores de variável.
Compilar com gcc -Wall fork-print.c -o fork-print
Carlos Maziero, DINF/UFPR 2020
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main ()
{
int retval, x ;
x = 0 ;
retval = fork () ;
printf (No processo %5d x vale %d\n, getpid(), x) ;
if ( retval < 0 )
{
perror (Erro) ;
exit (1) ;
}
else
if ( retval > 0 )
{
x = 0 ;
wait (0) ;
}
else
{
x++ ;
sleep (5) ;
}
printf (No processo %5d x vale %d\n, getpid(), x) ;
exit (0) ;
}
- Analise seu código e o comente detalhadamente.
Criação de processos em UNIX, com impressão de valores de variável.
Compilar com gcc -Wall fork-print.c -o fork-print
Carlos Maziero, DINF/UFPR 2020
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main ()
{
int retval, x ;
x = 0 ;
retval = fork () ;
printf (No processo %5d x vale %d\n, getpid(), x) ;
if ( retval < 0 )
{
perror (Erro) ;
exit (1) ;
}
else
if ( retval > 0 )
{
x = 0 ;
wait (0) ;
}
else
{
x++ ;
sleep (5) ;
}
printf (No processo %5d x vale %d\n, getpid(), x) ;
exit (0) ;
}
Passo 1
Beleza! O enunciado pede para a gente analisar e comentar detalhadamente o programa
fork-print.c
, que trata de criação de processos em UNIX usando fork()
, impressão de variáveis, wait()
e sleep()
. Para resolver isso, precisamos entender:- O que faz cada include e cada função (
fork()
,getpid()
,wait()
,sleep()
, etc.). - Como o valor da variável
x
é manipulado em cada processo (pai e filho). - A ordem de execução e de impressão, levando em conta o bloqueio do pai com
wait()
e o atraso do filho comsleep()
.
Passo 2
O começo do programa traz:
#include <unistd.h> // fork(), sleep()
#include <stdio.h> // printf(), perror()
#include <stdlib.h> // exit()
#include <sys/types.h>// tipos de dados (pid_t)
#include <sys/wait.h> // wait()
int main() {
int retval, x;
x = 0;
retval = fork();
// ...
}
Aqui a gente:
- Declara
x
(inicialmente 0) eretval
(onde vai ficar o retorno defork()
). - Chama
fork()
, que cria um processo filho.- Em um dos processos
retval > 0
(pai recebe PID do filho). - No outro
retval == 0
(filho). - Se der erro,
retval < 0
.
- Em um dos processos
Passo 3
Logo após o
fork()
vem o primeiro printf
:printf("No processo %5d x vale %d\n", getpid(), x);
Teoria rápida:
getpid()
retorna o identificador (PID) do processo que está chamando.- Os dois processos (pai e filho) herdam
x = 0
, pois cada um tem sua cópia da variável.
Então esseprintf
roda duas vezes (uma no pai, outra no filho), e ambos exibemx vale 0
. A ordem pode variar, mas normalmente o pai imprime primeiro, depois o filho.
Passo 4
Depois do primeiro
printf
, entra o bloco de decisão:if (retval < 0) {
perror("Erro");
exit(1);
}
else if (retval > 0) { // PAI
x = 0;
wait(0);
}
else { // FILHO
x++;
sleep(5);
}
Vamos destrinchar:
retval < 0
: erro na criação do processo (não é o nosso caso).retval > 0
é o pai:- Garante
x = 0
de novo (já era 0, mas reforça). - Chama
wait(0)
, que faz o pai “esperar” até o filho terminar tudo (inclusive o segundoprintf
eexit(0)
).
- Garante
retval == 0
é o filho:- Incrementa
x
para1
(agorano filho). - Faz
sleep(5)
, atrasando 5 segundos antes de continuar.
- Incrementa
Passo 5
Logo após esse
if/else
, vem o segundo printf
e o exit(0)
:printf("No processo %5d x vale %d\n", getpid(), x);
exit(0);
- No filho: depois do
sleep(5)
, ele imprimex = 1
e sai. - No pai: só depois que o filho termina (por causa do
wait(0)
) é que o pai imprimex = 0
e sai.
Resposta
Comentando tudo junto, a saída típica é (susceptível à ordem do primeiro par):
- “No processo PAI x vale 0”
- “No processo FILHO x vale 0”
– pai espera, filho fazsleep(5)
– - “No processo FILHO x vale 1”
- “No processo PAI x vale 0”
E o racional é: o
fork()
duplica o processo com cópia de x
, cada um roda o primeiro printf
, depois pai espera o filho (via wait
), o filho incrementa e dorme, imprime com x=1
e termina, então o pai acorda e imprime seu x=0
.