Mata a un niño Proceso en C

Jinku Hu 12 octubre 2023
  1. Utilice la señal SIGKILL para finalizar un proceso secundario en C
  2. Utilice la señal SIGTERM para finalizar un proceso secundario en C
Mata a un niño Proceso en C

Este artículo demostrará varios métodos sobre cómo eliminar un proceso hijo en C.

Utilice la señal SIGKILL para finalizar un proceso secundario en C

Hay múltiples señales diseñadas para terminar un proceso en el momento de su entrega, pero enviar la señal SIGKILL es el método más poderoso y seguro para hacerlo. Generalmente, un programa puede registrar funciones especiales llamadas manejadores de señales que se invocan automáticamente una vez que se entrega la señal correspondiente al programa. El usuario implementa el código de la función del controlador que generalmente realiza algún trabajo de limpieza para el programa. Además de los controladores de funciones, puede haber acciones predeterminadas en las señales entregadas, como bloquear e ignorar. Aunque, la señal SIGKILL no puede ser ignorada, bloqueada o manejada por la función dada. Por tanto, este método debería ser el último recurso al intentar finalizar un proceso.

La señal SIGKILL se puede enviar con una llamada al sistema kill. Sin embargo, tenga en cuenta que el controlador SIGTERM registrado en el siguiente ejemplo de código no puede capturar el SIGKILL entregado, e inmediatamente mata el proceso hijo dado.

El siguiente programa genera un proceso hijo y registra el controlador SIGTERM en él. Luego, el proceso hijo ejecuta un bucle infinito a menos que se entregue la señal, lo que invierte la variable de expresión while. En consecuencia, la señal SIGKILL entregada por el padre no invoca al controlador y finaliza el proceso hijo al instante.

#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);
}

Utilice la señal SIGTERM para finalizar un proceso secundario en C

Alternativamente, un proceso hijo se puede terminar usando la señal SIGTERM, que puede ser manejada por el programa. El siguiente ejemplo de código repite la implementación del programa anterior, excepto que sustituye la señal SIGKILL por SIGTERM. La función sigfillset se utiliza para evitar que otras señales interrumpan la ejecución de la función del controlador registrado. El código del controlador modifica la variable de tipo global sig_atomic_t que detiene el bucle while en el proceso hijo e imprime el valor de la variable count. Sin embargo, tenga en cuenta que siempre es mejor utilizar la llamada sigaction por encima de la función señal al registrar los controladores, ya que esta última no está especificada en detalle por el estándar 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);
}
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

Artículo relacionado - C Process