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) ;
}
  1. Analise seu código e o comente detalhadamente.

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 com sleep().

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:
  1. Declara x (inicialmente 0) e retval (onde vai ficar o retorno de fork()).
  2. 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.

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 esse printf roda duas vezes (uma no pai, outra no filho), e ambos exibem x 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:
    1. Garante x = 0 de novo (já era 0, mas reforça).
    2. Chama wait(0), que faz o pai “esperar” até o filho terminar tudo (inclusive o segundo printf e exit(0)).
  • retval == 0 é o filho:
    1. Incrementa x para 1 (agora x = 1 no filho).
    2. Faz sleep(5), atrasando 5 segundos antes de continuar.

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 imprime x = 1 e sai.
  • No pai: só depois que o filho termina (por causa do wait(0)) é que o pai imprime x = 0 e sai.

Resposta

Comentando tudo junto, a saída típica é (susceptível à ordem do primeiro par):
  1. “No processo PAI x vale 0”
  2. “No processo FILHO x vale 0”
    – pai espera, filho faz sleep(5)
  3. “No processo FILHO x vale 1”
  4. “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.

Exercícios relacionados