Contrôler le processus du démon à partir d'un autre processus en C
-
Utilisez les fonctions
fork
etsetsid
pour créer un processus démon -
Utilisez la fonction
daemon
pour créer un processus daemon
Cet article présentera plusieurs méthodes sur la façon de contrôler le processus démon à partir d’un autre processus dans C.
Utilisez les fonctions fork
et setsid
pour créer un processus démon
Les processus démons ont plusieurs caractéristiques, telles que ce sont des processus de longue durée et les démons peuvent être démarrés au démarrage du système. Le processus démon peut être contrôlé à partir des commandes utilisateur qui le forcent à se terminer ou à mettre en pause ou même à se désactiver au démarrage. Cependant, le scénario courant impliquerait un démon se terminant à l’arrêt du système à l’aide de certains scripts spécifiques au système. Les démons fonctionnent généralement en arrière-plan sans terminal de contrôle, et les programmes qui utilisent de telles fonctionnalités devraient implémenter divers gestionnaires de signaux pour gérer les interruptions externes.
Il existe plusieurs méthodes pour créer un processus démon et le surveiller, mais dans cet exemple, nous démontrons l’étape de création, où nous appelons une fonction fork
pour créer un processus enfant. Ensuite, le processus parent se termine, et l’enfant continue l’exécution en devenant l’enfant du processus init
(sur les systèmes Linux, le processus init
est le premier processus au démarrage). Le processus fils appelle la fonction setsid
pour démarrer une nouvelle session et supprimer le processus du terminal de contrôle. Enfin, nous appelons à nouveau fork
et sortons du processus parent pour nous assurer que notre démon n’acquiert pas de terminal de contrôle. Nous exécutons maintenant le processus démon; il est important d’enregistrer un gestionnaire de signal SIGTERM
pour effectuer tout nettoyage des ressources et sortie gracieuse lorsque le système ou l’utilisateur délivre l’interruption correspondante.
#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);
}
Utilisez la fonction daemon
pour créer un processus daemon
Même si l’exemple précédent montre un code apparemment correct, il ne dispose pas des étapes pour garantir que tous les descripteurs de fichiers ouverts hérités du parent sont fermés; le répertoire de travail a changé, redirige l’entrée standard vers /dev/null
, et ainsi de suite. Ces étapes garantissent que certaines fonctions n’échoueront pas si le démon les appelle et que certains comportements étranges ne sont pas observés.
Par exemple, si vous avez démarré le programme précédent depuis la fenêtre du terminal et que vous envoyez ensuite le signal SIGTERM
au processus démon, la fonction write
du gestionnaire de signal cleanupRoutine
s’imprimera sur le même terminal même après que la nouvelle invite est affiché. Ainsi, la fonction daemon
est fournie par la bibliothèque GNU C. Il prend automatiquement en charge les étapes ci-dessus pour garantir un contexte propre pour le processus démon nouvellement créé. La fonction démon
prend deux arguments entiers: le premier (s’il est égal à zéro) spécifiant si le répertoire de travail courant doit être changé en répertoire racine. Le deuxième entier (s’il est égal à zéro) indiquant si les flux d’E/S standard doivent être redirigés vers /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