GNU/Linux >> Tutoriels Linux >  >> Linux

Créer un démon sous Linux

man 7 daemon décrit comment créer un démon en détail. Ma réponse est juste un extrait de ce manuel.

Il existe au moins deux types de démons :

  1. démons SysV traditionnels (à l'ancienne),
  2. démons systemd (nouveau style).

Démons SysV

Si vous êtes intéressé par le démon SysV traditionnel, vous devez implémenter les étapes suivantes :

  1. Fermer tous les descripteurs de fichiers ouverts à l'exception de l'entrée standard , sortie , et erreur (c'est-à-dire les trois premiers descripteurs de fichier 0, 1, 2). Cela garantit qu'aucun descripteur de fichier transmis accidentellement ne reste dans le processus démon. Sous Linux, cela est mieux implémenté en itérant à travers /proc/self/fd , avec un retour d'itération du descripteur de fichier 3 à la valeur renvoyée par getrlimit() pour RLIMIT_NOFILE .
  2. Réinitialisez tous les gestionnaires de signaux à leur valeur par défaut. Pour ce faire, il est préférable de parcourir les signaux disponibles jusqu'à la limite de _NSIG et en les réinitialisant à SIG_DFL .
  3. Réinitialisez le masque de signal à l'aide de sigprocmask() .
  4. Nettoyez le bloc d'environnement, en supprimant ou en réinitialisant les variables d'environnement susceptibles d'avoir un impact négatif sur l'exécution du démon.
  5. Appelez le fork() , pour créer un processus d'arrière-plan.
  6. Dans l'enfant, appelez setsid() pour se détacher de n'importe quel terminal et créer une session indépendante.
  7. Chez l'enfant, appelez le fork() encore une fois, pour s'assurer que le démon ne pourra plus jamais réacquérir un terminal.
  8. Appelez le exit() dans le premier enfant, de sorte que seul le deuxième enfant (le processus démon réel) reste. Cela garantit que le processus démon est re-parenté à init/PID 1, comme tous les démons devraient l'être.
  9. Dans le processus démon, connectez /dev/null à l'entrée standard , sortie , et erreur .
  10. Dans le processus démon, réinitialisez le umask à 0, de sorte que les modes de fichier sont passés à open() , mkdir() et autres contrôlent directement le mode d'accès des fichiers et répertoires créés.
  11. Dans le processus démon, remplacez le répertoire courant par le répertoire racine (/ ), afin d'éviter que le démon bloque involontairement le démontage des points de montage.
  12. Dans le processus démon, écrivez le PID du démon (tel que renvoyé par getpid() ) à un fichier PID, par exemple /run/foobar.pid (pour un démon hypothétique "foobar") pour s'assurer que le démon ne peut pas être démarré plus d'une fois. Cela doit être implémenté de manière sans course afin que le fichier PID ne soit mis à jour que lorsqu'il est vérifié en même temps que le PID précédemment stocké dans le fichier PID n'existe plus ou appartient à un processus étranger.
  13. Dans le processus du démon, supprimez les privilèges, si possible et applicable.
  14. Depuis le processus démon, informez le processus d'origine démarré que l'initialisation est terminée. Cela peut être mis en œuvre via un canal sans nom ou un canal de communication similaire créé avant le premier fork() et donc disponible à la fois dans le processus d'origine et dans le processus démon.
  15. Appelez le exit() dans le processus d'origine. Le processus qui a appelé le démon doit pouvoir s'appuyer sur ce exit() arrive après l'initialisation est terminée et tous les canaux de communication externes sont établis et accessibles.

Notez cet avertissement :

Le BSD daemon() la fonction ne devrait pas être utilisé, car il n'implémente qu'un sous-ensemble de ces étapes.

Un démon qui doit fournir la compatibilité avec les systèmes SysV doivent mettre en œuvre le schéma indiqué ci-dessus. Cependant, il est recommandé de rendre ce comportement facultatif et configurable via un argument de ligne de commande pour faciliter le débogage ainsi que pour simplifier l'intégration dans les systèmes utilisant systemd.

Notez que daemon() n'est pas compatible POSIX.

Démons de nouveau style

Pour les démons de nouveau style, les étapes suivantes sont recommandées :

  1. Si SIGTERM est reçu, arrêtez le démon et quittez proprement.
  2. Si SIGHUP est reçu, rechargez les fichiers de configuration, le cas échéant.
  3. Fournissez un code de sortie correct du processus du démon principal, car il est utilisé par le système init pour détecter les erreurs et les problèmes de service. Il est recommandé de suivre le schéma de code de sortie tel que défini dans les recommandations LSB pour les scripts d'initialisation SysV.
  4. Si possible et applicable, exposez l'interface de contrôle du démon via le système D-Bus IPC et saisissez un nom de bus comme dernière étape de l'initialisation.
  5. Pour l'intégration dans systemd, fournissez un fichier d'unité .service contenant des informations sur le démarrage, l'arrêt et la maintenance du démon. Voir systemd.service(5) pour plus de détails.
  6. Autant que possible, comptez sur la fonctionnalité du système init pour limiter l'accès du démon aux fichiers, services et autres ressources, c'est-à-dire dans le cas de systemd, comptez sur le contrôle de limite de ressources de systemd au lieu d'implémenter le vôtre, comptez sur Le privilège de systemd abandonne le code au lieu de l'implémenter dans le démon, et similaire. Voir systemd.exec(5) pour les contrôles disponibles.
  7. Si D-Bus est utilisé, rendez votre démon activable par bus en fournissant un fichier de configuration d'activation de service D-Bus. Cela présente de multiples avantages :votre démon peut être démarré paresseusement à la demande; il peut être démarré en parallèle avec d'autres démons qui en ont besoin — ce qui maximise la parallélisation et la vitesse de démarrage ; votre démon peut être redémarré en cas d'échec sans perdre aucune demande de bus, car le bus met en file d'attente les demandes de services activables. Voir ci-dessous pour plus de détails.
  8. Si votre démon fournit des services à d'autres processus locaux ou clients distants via un socket, il doit être rendu activable par socket en suivant le schéma indiqué ci-dessous. Comme l'activation D-Bus, cela permet le démarrage à la demande des services ainsi qu'une meilleure parallélisation du démarrage des services. De plus, pour les protocoles sans état (tels que syslog, DNS), un démon implémentant l'activation basée sur les sockets peut être redémarré sans perdre une seule requête. Voir ci-dessous pour plus de détails.
  9. Le cas échéant, un démon doit informer le système init de l'achèvement du démarrage ou des mises à jour de statut via le sd_notify(3) interface.
  10. Au lieu d'utiliser le syslog() appel pour se connecter directement au service système syslog, un démon de nouveau style peut choisir de se connecter simplement à l'erreur standard via fprintf() , qui est ensuite transmis à syslog par le système init. Si des niveaux de journalisation sont nécessaires, ceux-ci peuvent être encodés en préfixant les lignes de journalisation individuelles avec des chaînes telles que "<4>" (pour le niveau de journalisation 4 "WARNING" dans le schéma de priorité syslog), en suivant un style similaire au printk() système de niveau. Pour plus de détails, voir sd-daemon(3) et systemd.exec(5) .

Pour en savoir plus, lisez l'intégralité du man 7 daemon .


Sous Linux, je souhaite ajouter un démon qui ne peut pas être arrêté et qui surveille les modifications du système de fichiers. Si des changements sont détectés, il doit écrire le chemin vers la console où il a été démarré + une nouvelle ligne.

Les démons fonctionnent en arrière-plan et (généralement...) n'appartiennent pas à un TTY, c'est pourquoi vous ne pouvez pas utiliser stdout/stderr comme vous le souhaitez probablement. Habituellement, un démon syslog (syslogd ) est utilisé pour consigner les messages dans les fichiers (débogage, erreur,...).

En plus de cela, il y a quelques étapes requises pour démoniser un processus.

Si je me souviens bien, ces étapes sont :

  • fork désactiver le processus parent et le laisser se terminer si le fork a réussi. -> Parce que le processus parent s'est terminé, le processus enfant s'exécute maintenant en arrière-plan.
  • setid - Créer une nouvelle session. Le processus appelant devient le leader de la nouvelle session et le leader du groupe de processus du nouveau groupe de processus. Le processus est maintenant détaché de son terminal de contrôle (CTTY).
  • Capter les signaux - Ignorer et/ou gérer les signaux.
  • fork à nouveau &laissez le processus parent se terminer pour vous assurer que vous vous débarrassez du processus de tête de session. (Seuls les animateurs de session peuvent obtenir à nouveau un ATS.)
  • chdir - Changer le répertoire de travail du démon.
  • umask - Modifiez le masque de mode de fichier en fonction des besoins du démon.
  • fermer - Fermez tous les descripteurs de fichiers ouverts qui peuvent être hérités du processus parent.

Pour vous donner un point de départ :regardez ce code squelette qui montre les étapes de base. Ce code peut désormais également être forké sur GitHub :Squelette de base d'un démon Linux

/*
 * daemonize.c
 * This example daemonizes a process, writes a few log messages,
 * sleeps 20 seconds and terminates afterwards.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>

static void skeleton_daemon()
{
    pid_t pid;

    /* Fork off the parent process */
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* On success: The child process becomes session leader */
    if (setsid() < 0)
        exit(EXIT_FAILURE);

    /* Catch, ignore and handle signals */
    //TODO: Implement a working signal handler */
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    /* Fork off for the second time*/
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* Set new file permissions */
    umask(0);

    /* Change the working directory to the root directory */
    /* or another appropriated directory */
    chdir("/");

    /* Close all open file descriptors */
    int x;
    for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
    {
        close (x);
    }

    /* Open the log file */
    openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
    skeleton_daemon();

    while (1)
    {
        //TODO: Insert daemon code here.
        syslog (LOG_NOTICE, "First daemon started.");
        sleep (20);
        break;
    }

    syslog (LOG_NOTICE, "First daemon terminated.");
    closelog();

    return EXIT_SUCCESS;
}


  • Compilez le code :gcc -o firstdaemon daemonize.c
  • Démarrer le démon :./firstdaemon
  • Vérifiez si tout fonctionne correctement :ps -xj | grep firstdaemon

  • Le résultat devrait ressembler à celui-ci :

+------+------+------+------+-----+-------+------+------+------+-----+
| PPID | PID  | PGID | SID  | TTY | TPGID | STAT | UID  | TIME | CMD |
+------+------+------+------+-----+-------+------+------+------+-----+
|    1 | 3387 | 3386 | 3386 | ?   |    -1 | S    | 1000 | 0:00 | ./  |
+------+------+------+------+-----+-------+------+------+------+-----+

Ce que vous devriez voir ici est :

  • Le démon n'a pas de terminal de contrôle (TTY =? )
  • L'identifiant du processus parent (PPID ) est 1 (Le processus d'initialisation)
  • Le PID !=SID ce qui signifie que notre processus n'est PAS le leader de la session
    (à cause du second fork())
  • Parce que PID !=SID, notre processus ne peut plus reprendre le contrôle d'un TTY

Lecture du journal système :

  • Recherchez votre fichier syslog. Le mien est ici :/var/log/syslog
  • Faites un :grep firstdaemon /var/log/syslog

  • Le résultat devrait ressembler à celui-ci :

  firstdaemon[3387]: First daemon started.
  firstdaemon[3387]: First daemon terminated.


Remarque : En réalité, vous voudriez également implémenter un gestionnaire de signaux et configurer correctement la journalisation (fichiers, niveaux de journalisation...).

Autres lectures :

  • Linux-UNIX-Programmierung - Allemand
  • Programmation du serveur démon Unix

Linux
  1. Comment tuer un processus zombie sous Linux

  2. Comment installer vtop sur Linux

  3. Linux - Bloquer l'accès réseau d'un processus ?

  4. Linux :transformer en service

  5. Démoniser un processus en shell ?

Comment tuer un processus sous Linux

Commande Ps sous Linux (liste des processus)

Commande Pstree sous Linux

Commande Kill sous Linux

Surveillance des processus sous Linux

Comment tuer un processus sous Linux