Uccidi un processo figlio in C

Jinku Hu 12 ottobre 2023
  1. Usa il segnale SIGKILL per terminare un processo figlio in C
  2. Usa il segnale SIGTERM per terminare un processo figlio in C
Uccidi un processo figlio in C

Questo articolo mostrerà più metodi su come uccidere un processo figlio in C.

Usa il segnale SIGKILL per terminare un processo figlio in C

Ci sono più segnali progettati per terminare un processo al momento della sua consegna, ma l’invio del segnale SIGKILL è il metodo più potente e sicuro per farlo. In genere, un programma può registrare funzioni speciali chiamate gestori di segnali che vengono richiamati automaticamente una volta che il segnale corrispondente viene consegnato al programma. L’utente implementa il codice della funzione del gestore che di solito esegue alcune operazioni di pulizia per il programma. Oltre ai gestori di funzioni, ci possono essere azioni predefinite sui segnali forniti come il blocco e l’ignoranza. Tuttavia, il segnale SIGKILL non può essere ignorato, bloccato o gestito dalla funzione data. Pertanto, questo metodo dovrebbe essere l’ultima risorsa quando si tenta di terminare un processo.

Il segnale SIGKILL può essere inviato con una chiamata di sistema kill. Nota però, il gestore SIGTERM registrato nel seguente esempio di codice non può catturare il SIGKILL consegnato, e uccide immediatamente il dato processo figlio.

Il seguente programma genera un processo figlio e vi registra il gestore SIGTERM. Quindi, il processo figlio esegue un bucle infinito a meno che il segnale non venga consegnato, il che capovolge la variabile di espressione while. Di conseguenza, il segnale SIGKILL fornito dal genitore non richiama il gestore e termina istantaneamente il processo figlio.

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

volatile sig_atomic_t shutdown_flag = 1;

void cleanupRoutine(int signal_number) { shutdown_flag = 0; }

int main(void) {
  int wstatus;

  pid_t c_pid = fork();
  if (c_pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (c_pid == 0) {
    printf("printed from child process - %d\n", getpid());

    int count = 0;

    struct sigaction sigterm_action;
    memset(&sigterm_action, 0, sizeof(sigterm_action));
    sigterm_action.sa_handler = &cleanupRoutine;
    sigterm_action.sa_flags = 0;

    // Mask other signals from interrupting SIGTERM handler
    if (sigfillset(&sigterm_action.sa_mask) != 0) {
      perror("sigfillset");
      exit(EXIT_FAILURE);
    }
    // Register SIGTERM handler
    if (sigaction(SIGTERM, &sigterm_action, NULL) != 0) {
      perror("sigaction SIGTERM");
      exit(EXIT_FAILURE);
    }

    while (shutdown_flag) {
      count += 1;
    }
    printf("count = %d\n", count);

    exit(EXIT_SUCCESS);
  } else {
    printf("printed from parent process - %d\n", getpid());
    int ret;

    sleep(5);

    ret = kill(c_pid, SIGKILL);
    if (ret == -1) {
      perror("kill");
      exit(EXIT_FAILURE);
    }

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

  exit(EXIT_SUCCESS);
}

Usa il segnale SIGTERM per terminare un processo figlio in C

In alternativa, un processo figlio può essere terminato utilizzando il segnale SIGTERM, che può essere gestito dal programma. L’esempio di codice successivo ripete l’implementazione del programma precedente tranne per il fatto che sostituisce il segnale SIGKILL con SIGTERM. La funzione sigfillset viene utilizzata per impedire che altri segnali interrompano l’esecuzione della funzione handler registrata. Il codice del gestore modifica la variabile di tipo globale sig_atomic_t che interrompe il cicli while nel processo figlio e stampa il valore della variabile count. Attenzione però, è sempre meglio usare la chiamata sigaction sopra la funzione signal quando si registrano i gestori, poiché quest’ultima non è specificata in dettaglio dallo standard POSIX.

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

volatile sig_atomic_t shutdown_flag = 1;

void cleanupRoutine(int signal_number) { shutdown_flag = 0; }

int main(void) {
  int wstatus;

  pid_t c_pid = fork();
  if (c_pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (c_pid == 0) {
    printf("printed from child process - %d\n", getpid());

    int count = 0;
    struct sigaction sigterm_action;
    memset(&sigterm_action, 0, sizeof(sigterm_action));
    sigterm_action.sa_handler = &cleanupRoutine;
    sigterm_action.sa_flags = 0;

    // Mask other signals from interrupting SIGTERM handler
    if (sigfillset(&sigterm_action.sa_mask) != 0) {
      perror("sigfillset");
      exit(EXIT_FAILURE);
    }
    // Register SIGTERM handler
    if (sigaction(SIGTERM, &sigterm_action, NULL) != 0) {
      perror("sigaction SIGTERM");
      exit(EXIT_FAILURE);
    }

    while (shutdown_flag) {
      count += 1;
    }
    printf("count = %d\n", count);

    exit(EXIT_SUCCESS);
  } else {
    printf("printed from parent process - %d\n", getpid());
    int ret;

    sleep(5);

    ret = kill(c_pid, SIGTERM);
    if (ret == -1) {
      perror("kill");
      exit(EXIT_FAILURE);
    }

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

  exit(EXIT_SUCCESS);
}
Autore: 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

Articolo correlato - C Process