[17]. Elabore um programa em linguagem C em que três processos executam paralelamente as seguintes ações:
- Pai - Imprime os números de 1 a 50 , com um intervalo de 2 segundos entre cada número.
- Após imprimir todos os números, imprime a frase “Processo pai vai morrer”.
- Filho1 - Imprime os números de 100 a 200 , com um intervalo de 1 segundo entre cada número.
- Antes de imprimir os números, imprime a frase “Filho 1 foi criado”.
- Após imprimir todos os números, imprime a frase “Filho 1 vai morrer”.
Importante:
- Em cada printf os processos devem imprimir o seu pid e o pid do seu pai.
- Use a instrução puts("…") depois da linha do printf().
- DICA: sleep(1) bloqueia o processo por 1 segundo.
Passo 1
O enunciado pede um programa em C que crie três processos que rodem em paralelo e realizem ações específicas. As informações fornecidas são:
- O processo pai deve imprimir os números de 1 a 50, com intervalo de 2 segundos entre cada impressã o.
- Depois de imprimir os 50 números, o pai deve imprimir a frase “Processo pai vai morrer”.
- O processo “Filho 1” deve imprimir a frase “Filho 1 foi criado” antes de começar a imprimir números.
- O “Filho 1” deve imprimir os números de 100 a 200, com intervalo de 1 segundo entre cada número.
- Depois de imprimir todos os números, o “Filho 1” deve imprimir a frase “Filho 1 vai morrer”.
- Em cada printf todos os processos devem imprimir o seu PID e o PID do seu pai.
- Após cada printf deve ser chamada a instrução puts("…") (ou seja, usar puts logo depois do printf).
- DICA: usar sleep(1) para esperar 1 segundo.
Observação importante para a implementação (decisão prática, pois o enunciado pede três processos mas descreve apenas o pai e um filho): o programa vai criar exatamente três processos distintos: o processo pai e dois filhos. O primeiro filho será o “Filho 1” que executa a tarefa de imprimir 100…200. O segundo filho será criado para completar o número de processos exigido (ele terá uma ação simples, mostrando sua criação e morte). Vou justificar essa escolha e mostrar como criar exatamente três processos sem criar processos extras acidentalmente.
Passo 2
Por que criar exatamente três processos e como evitar criar mais processos do que o necessário?
- Se o pai executar fork() uma vez, teremos dois processos (pai e um filho).
- Se colocarmos outro fork() sem controle, tanto o pai quanto o primeiro filho executarão esse segundo fork(), e então teremos quatro processos (ou mais), o que não é desejado.
- Para ter exatamente três processos, a forma simples e segura é: o pai faz um primeiro fork() para criar o Filho 1; o pai, somente ele, faz um segundo fork() para criar o Filho 2. Assim temos exatamente: pai, filho1 e filho2. No código isso se garante testando o valor retornado de fork() e fazendo o segundo fork() apenas no ramo do processo pai (quando fork retorna > 0).
Vou usar essa estratégia porque é direta e evita forks descontrolados.
Passo 3
Conceitos essenciais que vou usar (explicação didática, passo a passo):
- fork():
- É a chamada que duplica o processo atual. Ela retorna um valor inteiro:
- Retorna um valor negativo se houve erro.
- Retorna zero (0) no processo filho recém-criado.
- Retorna o PID do filho no processo pai.
- Exemplo prático:
pid_t pid = fork();- Se
pid == 0→ estamos no filho. - Se
pid > 0→ estamos no pai epidé o PID do filho criado.
- É a chamada que duplica o processo atual. Ela retorna um valor inteiro:
- getpid() e getppid():
getpid()retorna o PID (identificador) do processo atual.getppid()retorna o PID do processo pai (o processo que fez o fork que gerou este processo).- Vamos imprimir esses valores em cada mensagem para cumprir a exigência.
- sleep():
sleep(n)bloqueia o processo pornsegundos (inteiros).- Usaremos
sleep(2)no pai entre cada número esleep(1)no Filho 1.
- printf() e puts():
printf()permite imprimir mensagens formatadas, com\nao final se desejado.- O enunciado exige que após cada
printf()seja chamada uma instruçãoputs("..."). Vou chamarputs("")(uma string vazia) logo depois doprintf()para inserir uma nova linha extra, como pedido. (Se preferir outra mensagem literal, pode-se ajustar.)
- Gestão de erros:
- Verificaremos o retorno de
fork()para detectar erros (imprimir mensagem eexit(1)em caso de erro).
- Verificaremos o retorno de
Passo 4
Estrutura lógica do programa (pseudocódigo com explicação):
- Incluir cabeçalhos necessários:
<stdio.h>,<stdlib.h>,<unistd.h>,<sys/types.h>. - No
main():- Fazer
pid_t pid1 = fork(); - Testar
pid1:- Se
pid1 < 0: erro. - Se
pid1 == 0: este é o Filho 1 → executar comportamento do Filho 1 (mensagem de criação → loop 100…200 com sleep(1) → mensagem de morte → exit). - Se
pid1 > 0: este é o pai → continuar para criar o Filho 2.
- Se
- No ramo do pai (quando
pid1 > 0), fazerpid_t pid2 = fork();- Se
pid2 < 0: erro. - Se
pid2 == 0: este é o Filho 2 → comportamento simples (p.ex. imprimir criação e morrer). - Se
pid2 > 0: continua o pai → executar comportamento do pai (loop 1…50 com sleep(2) → mensagem de morte).
- Se
- Fazer
- Após cada
printf()chamarputs("")para obedecer à exigência. - Não é obrigatório o pai aguardar (
wait) os filhos aqui, pois o enunciado pede paralelismo. Se o pai terminar antes de algum filho, esse filho se tornará órfão (adotado pelo init). Isso é aceitável para este exercício. Se quiser que o pai espere os filhos, usarwaitpid(); mas não farei isso para manter os processos realmente concorrentes e obedecer rigorosamente as durações pedidas.
Passo 5
Detalhes de implementação e convenções de impressão (para a prova: o que exatamente escrever em cada printf):
- Em cada printf vou colocar o texto que identifica o processo, o número atual (se for em loop), e os PIDs. Exemplos:
- Pai imprimindo um número i:
- printf(“Pai: %d | pid=%d ppid=%d\n”, i, (int)getpid(), (int)getppid());
- puts("");
- Filho 1 mensagem de criação:
- printf(“Filho 1 foi criado | pid=%d ppid=%d\n”, (int)getpid(), (int)getppid());
- puts("");
- Filho 1 imprimindo um número j:
- printf(“Filho 1: %d | pid=%d ppid=%d\n”, j, (int)getpid(), (int)getppid());
- puts("");
- Frases finais:
- For parent: printf(“Processo pai vai morrer | pid=%d ppid=%d\n”, …); puts("");
- For child1: printf(“Filho 1 vai morrer | pid=%d ppid=%d\n”, …); puts("");
- Pai imprimindo um número i:
- Observação sobre casts:
getpid()egetppid()retornampid_t. Para imprimir com%dvou converter explicitamente para(int)para portabilidade simples.
Passo 6
Possíveis dúvidas na hora da prova e como resolvê-las rapidamente:
- “Como faço para criar exatamente dois filhos?” → Lembre-se: faça o segundo
fork()apenas no ramo do pai (verifiqueif (pid1 > 0)antes de fazer o segundofork()). - “E se eu fizer o segundo fork sem checar?” → Você criará processos extras (o primeiro filho também executará o segundo fork), produzindo 4 processos em vez de 3.
- “Preciso usar wait()?” → Não é obrigatório aqui, pois o enunciado quer paralelismo; se quiser evitar processos zumbis, pode chamar
waitpid()no pai depois de terminar suas impressões. Mas cuidado:waitbloquearia o pai até filhos terminarem se usado de modo bloqueante. - “E o puts depois do printf?” → Coloque uma linha
puts("");logo após cadaprintf(...) ;como o enunciado exige.
Passo 7
Resumo final antes de ver o código: a estratégia escolhida é
- pai faz primeiro fork → cria Filho 1 (que fará 100…200),
- apenas o pai faz o segundo fork → cria Filho 2 (processinho auxiliar),
- cada processo executa seu bloco de código independente,
- em todos os printf imprime pid e ppid e logo após chama puts(""), e os sleeps obedecem aos requisitos.
Resposta
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
pid_t pid1, pid2;
/* Primeiro fork: cria Filho 1 */
pid1 = fork();
if (pid1 < 0) {
perror("fork");
exit(1);
}
if (pid1 == 0) {
/* RAMO: Filho 1 */
printf("Filho 1 foi criado | pid=%d ppid=%d\n", (int)getpid(), (int)getppid());
puts("");
for (int i = 100; i <= 200; ++i) {
printf("Filho 1: %d | pid=%d ppid=%d\n", i, (int)getpid(), (int)getppid());
puts("");
sleep(1); /* intervalo de 1 segundo */
}
printf("Filho 1 vai morrer | pid=%d ppid=%d\n", (int)getpid(), (int)getppid());
puts("");
exit(0);
}
/* RAMO: pai continua aqui (pid1 > 0). Só o pai faz o segundo fork. */
pid2 = fork();
if (pid2 < 0) {
perror("fork");
exit(1);
}
if (pid2 == 0) {
/* RAMO: Filho 2 (processo auxiliar, apenas para completar 3 processos) */
printf("Filho 2 foi criado | pid=%d ppid=%d\n", (int)getpid(), (int)getppid());
puts("");
/* Comportamento simples: espera um pouco e morre */
sleep(5);
printf("Filho 2 vai morrer | pid=%d ppid=%d\n", (int)getpid(), (int)getppid());
puts("");
exit(0);
}
/* RAMO: Pai original (pid1 > 0 e pid2 > 0) */
for (int i = 1; i <= 50; ++i) {
printf("Pai: %d | pid=%d ppid=%d\n", i, (int)getpid(), (int)getppid());
puts("");
sleep(2); /* intervalo de 2 segundos */
}
printf("Processo pai vai morrer | pid=%d ppid=%d\n", (int)getpid(), (int)getppid());
puts("");
/* Opcional: não esperar pelos filhos para manter paralelismo.
Se quiser esperar, usar waitpid(pid1, NULL, 0); waitpid(pid2, NULL, 0); */
return 0;
}