Dado o programa fork-execve.c a seguir:
fork-execve.c
/*
Criação de processos em UNIX, com execução de outro binário
Compilar com gcc -Wall fork-execve.c -o fork-execve
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 argc, char *argv[], char *envp[])
{
int retval ;
printf (Ola, sou o processo %5d\n, getpid()) ;
retval = fork () ;
printf ([retval: %5d] sou %5d, filho de %5d\n, retval, getpid(), getppid()) ;
if ( retval < 0 ) // erro no fork ()
{
perror (Erro: ) ;
exit (1) ;
}
else
if ( retval > 0 ) // sou o processo pai
wait (0) ;
else // sou o processo filho
{
execve (/bin/date, argv, envp) ;
perror (Erro) ;
}
printf (Tchau de %5d!\n, getpid()) ;
exit (0) ;
}
- Analise seu código e o comente detalhadamente.
fork-execve.c
/*
Criação de processos em UNIX, com execução de outro binário
Compilar com gcc -Wall fork-execve.c -o fork-execve
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 argc, char *argv[], char *envp[])
{
int retval ;
printf (Ola, sou o processo %5d\n, getpid()) ;
retval = fork () ;
printf ([retval: %5d] sou %5d, filho de %5d\n, retval, getpid(), getppid()) ;
if ( retval < 0 ) // erro no fork ()
{
perror (Erro: ) ;
exit (1) ;
}
else
if ( retval > 0 ) // sou o processo pai
wait (0) ;
else // sou o processo filho
{
execve (/bin/date, argv, envp) ;
perror (Erro) ;
}
printf (Tchau de %5d!\n, getpid()) ;
exit (0) ;
}
Passo 1
Resumo do enunciado: a gente tem um programa em C chamado fork-execve.c que mostra como criar processos no UNIX com
fork()
, trocar a imagem do processo filho com execve()
e aguardar o filho com wait()
. O conteúdo da matéria é “Controle de processos em UNIX” e a gente vai precisar entender as chamadas de sistema:fork()
: cria um processo filhoexecve()
: substitui o processo atual por outro executávelwait()
: faz o pai esperar o término do filhogetpid()
,getppid()
: obtêm IDs de processos
Passo 2
Analisando os includes e a assinatura de
main
:#include <unistd.h> // protótipos de fork, execve, getpid, getppid
#include <stdio.h> // protótipos de printf, perror
#include <stdlib.h> // protótipos de exit
#include <sys/types.h> // tipos de dados (pid_t, etc.)
#include <sys/wait.h> // protótipo de wait
int main(int argc, char *argv[], char *envp[])
Comentário:
<unistd.h>
traz as funções de baixo nível do UNIX.<stdio.h>
e<stdlib.h>
são padrões de C.<sys/types.h>
e<sys/wait.h>
são necessários parawait()
.argc
,argv[]
eenvp[]
são, respectivamente, o número de argumentos, o vetor de argumentos e o vetor de variáveis de ambiente.
Passo 3
Primeiros prints e a chamada de
fork()
:int retval;
printf("Ola, sou o processo %5d\n", getpid());
retval = fork();
printf("[retval: %5d] sou %5d, filho de %5d\n",
retval, getpid(), getppid());
Comentário:
getpid()
retorna o PID (Process ID) do processo atual. O primeiroprintf
mostra esse PID.fork()
cria um clone do processo. Ela devolve:< 0
se deu erro== 0
no processo filho> 0
no processo pai (o valor é o PID do filho)
- Depois do
fork()
os dois processos (pai e filho) continuam a execução a partir da próxima linha. O segundoprintf
mostraretval
(que indica se é pai ou filho), o PID atual e o PID do pai (getppid()
).
Passo 4
Tratando erro no
fork()
:if (retval < 0) {
// erro no fork()
perror("Erro: ");
exit(1);
}
Comentário:
- Se
retval < 0
, ocorreu um problema ao criar o filho. perror
imprime no stderr uma mensagem explicando o erro com base emerrno
.exit(1)
finaliza o programa informando que houve falha.
Passo 5
Divisão de comportamento de pai e filho:
else if (retval > 0) {
// sou o processo pai
wait(0);
} else {
// sou o processo filho
execve("/bin/date", argv, envp);
perror("Erro");
}
Comentário:
retval > 0
: estamos no processo pai. Chamamoswait(0)
, que bloqueia o pai até o filho terminar.retval == 0
: estamos no processo filho.execve("/bin/date", argv, envp)
substitui a imagem do filho pelo binário/bin/date
, passando os mesmosargv
eenvp
. Se der tudo certo, a partir daí o código do filho é o dedate
.- Se
execve
falhar, ela retorna e a gente cai emperror("Erro")
para reportar o problema.
Passo 6
Print final e saída:
printf("Tchau de %5d!\n", getpid());
exit(0);
Comentário:
- Essa parte é executada tanto pelo pai (depois que o filho termina, por causa do
wait
) quanto pelo filho, caso oexecve
falhe (porque umexecve
bem‐sucedido não retorna). getpid()
mostra quem está dizendo “Tchau”.exit(0)
termina o processo com código de sucesso.
Resposta
O código acima está comentado linha a linha, explicando cada chamada de sistema e fluxo de execução entre pai e filho, conforme solicitado.