Use a função waitpid em C

Jinku Hu 12 outubro 2023
  1. Use a função waitpid para monitorar o status do processo filho em C
  2. Use macros para exibir o status de espera do processo filho em C
Use a função waitpid em C

Este artigo demonstrará vários métodos sobre como usar a função waitpid em C.

Use a função waitpid para monitorar o status do processo filho em C

Em sistemas baseados em Unix, existe a noção de um processo que é simplesmente uma instância em execução de um programa. O processo pode criar outros processos usando a chamada de sistema fork e executar a parte dada do código. Observe que, para este tópico, as chamadas do sistema são serviços do sistema operacional fornecidos ao usuário como funções do tipo C. Geralmente, em muitos cenários, um programa é necessário para monitorar os processos que ele criou, chamados filhos. A família de funções wait é aquela que fornece esta funcionalidade e waitpid é uma delas.

A chamada de sistema wait tem múltiplas limitações e, para cobrir recursos mais avançados, a função waitpid deve ser utilizada. Ou seja, se um processo cria vários filhos e o pai precisa monitorar um filho específico, apenas waitpid pode fazer isso. No exemplo a seguir, implementamos uma função chamada spawnChild que cria um novo processo filho e executa um programa diferente. Para uma boa demonstração do exemplo, estamos executando um programa top (disponível em quase todos os sistemas baseados em Unix) que está em execução até que o usuário o encerre. Uma vez que a função retorna no processo pai, armazenamos um ID do processo filho e o passamos para a função waitpid para monitorar o status.

O waitpid leva três argumentos, o primeiro dos quais é o número de identificação do processo (pid). O PID pode ter vários valores predefinidos com diferentes efeitos, mas, neste caso, vamos apenas mencionar -1 e >0. O valor -1 pode ser passado para monitorar qualquer processo filho que muda seu estado primeiro, que é usado para implementar a funcionalidade wait. >0 implica que o valor deve ser o ID do processo real que foi retornado da função fork, que por sua vez é usado para monitorar apenas o processo filho específico. O segundo argumento é do tipo ponteiro int e devemos declarar uma variável inteira para passar seu endereço para a função. waitpid, por outro lado, irá armazenar as informações de status do filho na variável int fornecida, que então pode ser decodificada usando as macros predefinidas. O último argumento é do tipo int e é usado para especificar determinados eventos de processo filho a serem monitorados, além dos padrões.

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

pid_t spawnChild(const char* program, char** arg_list) {
  pid_t ch_pid = fork();
  if (ch_pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (ch_pid > 0) {
    printf("spawn child with pid - %d\n", ch_pid);
    return ch_pid;
  } else {
    execvp(program, arg_list);
    perror("execve");
    exit(EXIT_FAILURE);
  }
}

int main(void) {
  const char* args[] = {"top", NULL, NULL};

  pid_t child;
  int wstatus;

  child = spawnChild("top", args);

  if (waitpid(child, &wstatus, WUNTRACED | WCONTINUED) == -1) {
    perror("waitpid");
    exit(EXIT_FAILURE);
  }

  exit(EXIT_SUCCESS);
}

Use macros para exibir o status de espera do processo filho em C

Observe que o processo pai é suspenso quando a função waitpid é chamada e não retoma a execução até que o processo filho monitorado mude de estado. O próximo exemplo mostra a chamada waitpid com os argumentos WUNTRACED e WCONTINUED, o que implica que um pai monitora se o filho foi interrompido ou continuado por sinais correspondentes. Além disso, implementamos a função printWaitStatus que pode ser chamada para imprimir o status do filho recuperado. Observe que ele está usando macros do tipo W* definidas no cabeçalho <sys/wait.h> para extrair as informações codificadas da variável inteira status.

Existem algumas definições de pré-processador condicionais, uma vez que nem todas as macros estão disponíveis em todas as plataformas, garantindo assim que a função é portátil e compila com êxito independentemente.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

pid_t spawnChild(const char* program, char** arg_list) {
  pid_t ch_pid = fork();
  if (ch_pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (ch_pid > 0) {
    printf("spawn child with pid - %d\n", ch_pid);
    return ch_pid;
  } else {
    execvp(program, arg_list);
    perror("execve");
    exit(EXIT_FAILURE);
  }
}

void printWaitStatus(int status) {
  if (WIFEXITED(status)) {
    printf("child exited, status=%d\n", WEXITSTATUS(status));
  } else if (WIFSIGNALED(status)) {
    printf("child killed by signal %d (%s)", WTERMSIG(status),
           strsignal(WTERMSIG(status)));
#ifdef WCOREDUMP
    if (WCOREDUMP(status)) printf(" (core dumped)");
#endif
    printf("\n");
  } else if (WIFSTOPPED(status)) {
    printf("child stopped by signal %d (%s)\n", WSTOPSIG(status),
           strsignal(WSTOPSIG(status)));
#ifdef WIFCONTINUED
  } else if (WIFCONTINUED(status)) {
    printf("child continued\n");
#endif
  } else {
    printf("status=%x\n", (unsigned int)status);
  }
}

int main(void) {
  const char* args[] = {"top", NULL, NULL};

  pid_t child;
  int wstatus;

  child = spawnChild("top", args);

  if (waitpid(child, &wstatus, WUNTRACED | WCONTINUED) == -1) {
    perror("waitpid");
    exit(EXIT_FAILURE);
  }

  printWaitStatus(wstatus);

  exit(EXIT_SUCCESS);
}
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Artigo relacionado - C Process