Control del proceso del demonio desde otro proceso en C

Jinku Hu 12 octubre 2023
  1. Utilice las funciones fork y setsid para crear un proceso daemon
  2. Utilice la función daemon para crear un proceso daemon
Control del proceso del demonio desde otro proceso en C

Este artículo presentará varios métodos sobre cómo controlar el proceso del demonio desde otro proceso en C.

Utilice las funciones fork y setsid para crear un proceso daemon

Los procesos daemon tienen varias características, como que son procesos de larga ejecución y los daemons pueden iniciarse al inicio del sistema. El proceso del demonio se puede controlar desde los comandos del usuario que lo obligan a finalizar, pausar o incluso deshabilitar al inicio. Aunque, el escenario común implicaría un demonio que termina en el apagado del sistema utilizando algunos scripts específicos del sistema. Los demonios generalmente se ejecutan en segundo plano sin una terminal de control, y los programas que emplean tales características deben implementar varios manejadores de señales para lidiar con interrupciones externas.

Hay varios métodos para crear un proceso demonio y monitorearlo, pero en este ejemplo, demostramos la etapa de creación, donde llamamos a una función de fork para crear un proceso hijo. Luego, el proceso padre sale y el hijo continúa la ejecución a medida que se convierte en hijo del proceso init (en los sistemas Linux, el proceso init es el primer proceso en el inicio). El proceso hijo llama a la función setsid para iniciar una nueva sesión y eliminar el proceso del terminal de control. Finalmente, llamamos a fork una vez más y salimos del proceso padre para asegurarnos de que nuestro demonio no adquirirá una terminal de control. Ahora estamos ejecutando el proceso del demonio; Es importante registrar un manejador de señales SIGTERM para realizar cualquier limpieza de recursos y una salida elegante cuando el sistema o el usuario entregue la interrupción correspondiente.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define errExit(msg)    \
  do {                  \
    perror(msg);        \
    exit(EXIT_FAILURE); \
  } while (0)

void cleanupRoutine(int signal_number) {
  write(STDERR_FILENO, "hello", 5);
  _exit(EXIT_SUCCESS);
}

int main(int argc, char *argv[]) {
  fprintf(stderr, "[pid - %d] running...\n", getpid());

  switch (fork()) {
    case -1:
      errExit("fork");
    case 0:
      break;
    default:
      _exit(EXIT_SUCCESS);
  }
  fprintf(stderr, "[pid - %d] running...\n", getpid());

  if (setsid() == -1) errExit("setsid");

  switch (fork()) {
    case -1:
      errExit("fork");
    case 0:
      break;
    default:
      _exit(EXIT_SUCCESS);
  }
  fprintf(stderr, "[pid - %d] running...\n", getpid());

  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 (1) {
    getpid();
  }

  exit(EXIT_SUCCESS);
}

Utilice la función daemon para crear un proceso daemon

Aunque el ejemplo anterior demuestra un código aparentemente correcto, carece de los pasos para garantizar que todos los descriptores de archivos abiertos heredados del padre estén cerrados; el directorio de trabajo cambió, redirigir la entrada estándar a /dev/null, y así sucesivamente. Estos pasos garantizan que ciertas funciones no fallarán si el demonio las invoca, y además no se observa algún comportamiento extraño.

Por ejemplo, si inició el programa anterior desde la ventana del terminal y luego envía la señal SIGTERM al proceso del demonio, la función write del manejador de señales cleanupRoutine se imprimirá en el mismo terminal incluso después de que se muestre el nuevo mensaje. desplegado. Por lo tanto, la función daemon es proporcionada por la biblioteca GNU C. Automáticamente se encarga de los pasos anteriores para garantizar un contexto limpio para el proceso de demonio recién creado. La función daemon toma dos argumentos enteros: el primero (si es igual a cero) especifica si el directorio de trabajo actual debe cambiarse al directorio raíz. El segundo entero (si es igual a cero) que indica si los flujos de E/S estándar deben redirigirse a /dev/null.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define errExit(msg)    \
  do {                  \
    perror(msg);        \
    exit(EXIT_FAILURE); \
  } while (0)

void cleanupRoutine(int signal_number) {
  write(STDERR_FILENO, "hello", 5);
  _exit(EXIT_SUCCESS);
}

int main(int argc, char *argv[]) {
  fprintf(stderr, "[pid - %d] running...\n", getpid());

  if (daemon(0, 0) == -1) errExit("daemon");

  fprintf(stderr, "[pid - %d] running...\n", getpid());

  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) errExit("sigfillset");

  // Register SIGTERM handler
  if (sigaction(SIGTERM, &sigterm_action, NULL) != 0) errExit("sigaction");

  while (1) {
    getpid();
  }

  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