[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 e pid é o PID do filho criado.
  • 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 por n segundos (inteiros).
    • Usaremos sleep(2) no pai entre cada número e sleep(1) no Filho 1.
  • printf() e puts():
    • printf() permite imprimir mensagens formatadas, com \n ao final se desejado.
    • O enunciado exige que após cada printf() seja chamada uma instrução puts("..."). Vou chamar puts("") (uma string vazia) logo depois do printf() 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 e exit(1) em caso de erro).

Passo 4

Estrutura lógica do programa (pseudocódigo com explicação):
  1. Incluir cabeçalhos necessários: <stdio.h>, <stdlib.h>, <unistd.h>, <sys/types.h>.
  2. 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.
    • No ramo do pai (quando pid1 > 0), fazer pid_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).
  3. Após cada printf() chamar puts("") para obedecer à exigência.
  4. 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, usar waitpid(); 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("");
  • Observação sobre casts: getpid() e getppid() retornam pid_t. Para imprimir com %d vou 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 (verifique if (pid1 > 0) antes de fazer o segundo fork()).
  • “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: wait bloquearia o pai até filhos terminarem se usado de modo bloqueante.
  • “E o puts depois do printf?” → Coloque uma linha puts(""); logo após cada printf(...) ; 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;
}

Exercícios relacionados