Control del proceso del demonio desde otro proceso en C
-
Utilice las funciones
fork
ysetsid
para crear un proceso daemon -
Utilice la función
daemon
para crear un proceso daemon
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);
}
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