140 votes

Création d'un démon sous Linux

Sous Linux, je veux 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 d'accès à la console où il a été lancé plus un saut de ligne.

J'ai déjà le code de modification du système de fichiers presque prêt, mais je n'arrive pas à trouver comment créer un démon.

Mon code vient d'ici : http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

Que faire après la fourchette ?

int main (int argc, char **argv) {

  pid_t pID = fork();
  if (pID == 0)  {              // child
          // Code only executed by child process    
      sIdentifier = "Child Process: ";
    }
    else if (pID < 0) {
        cerr << "Failed to fork" << endl;
        exit(1);
       // Throw exception
    }
    else                                   // parent
    {
      // Code only executed by parent process

      sIdentifier = "Parent Process:";
    }       

    return 0;
}

264voto

Pascal Werkl Points 2608

Sous Linux, je veux 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 d'accès à la console où il a été lancé + une nouvelle ligne.

Les démons travaillent 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. Habituellement, un démon syslog ( syslogd ) est utilisé pour enregistrer des messages dans des fichiers (debug, erreur,...).

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


Si je me souviens bien, ces étapes sont les suivantes :

  • fourchette le processus parent et le laisser se terminer si la bifurcation a réussi. -> Comme le processus parent s'est terminé, le processus enfant fonctionne maintenant en arrière-plan.
  • setid - Créez une nouvelle session. Le processus appelant devient le leader de la nouvelle session et le leader du nouveau groupe de processus. Le processus est maintenant détaché de son terminal de contrôle (CTTY).
  • Signaux de capture - Ignorer et/ou traiter les signaux.
  • encore une fourchette et laissez le processus parent se terminer pour vous assurer que vous vous débarrassez du processus de direction de session. (Seuls les leaders de session peuvent à nouveau obtenir un ATS).
  • chdir - Changer le répertoire de travail du démon.
  • umask - Changez le masque de mode de fichier en fonction des besoins du démon.
  • fermer - Ferme 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 squelette de code qui montre les étapes de base. Ce code peut maintenant aussi ê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 que tout fonctionne correctement : ps -xj | grep firstdaemon

  • Le résultat devrait être similaire à 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'ID du processus parent ( PPID ) est 1 (Le processus d'init)
  • El PID != SID ce qui signifie que notre processus n'est PAS le leader de la session.
    (à cause du deuxième fork())
  • Parce que PID != SID notre processus ne peut plus prendre le contrôle d'un ATS

Lecture du syslog :

  • Localisez votre fichier syslog. Le mien est ici : /var/log/syslog

  • Faites un : grep firstdaemon /var/log/syslog

  • Le résultat devrait être similaire à celui-ci :

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

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

Pour en savoir plus :

43voto

patryk.beza Points 2718

man 7 daemon décrit comment créer un démon de manière très détaillée. Ma réponse n'est qu'un extrait de ce manuel.

Il existe au moins deux types de démons :

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

Daemons SysV

Si vous êtes intéressé par le traditionnel SysV vous devez implémenter l'option les étapes suivantes :

  1. Fermer tous les descripteurs de fichiers ouverts, sauf le standard entrée , sortie y erreur (c'est-à-dire les trois premiers descripteurs de fichiers 0, 1, 2). Cela permet de s'assurer qu'aucun descripteur de fichier passé accidentellement ne reste dans le processus du démon. Sous Linux, la meilleure façon de procéder est d'itérer dans le fichier /proc/self/fd avec une solution de repli consistant à itérer à partir du descripteur de fichier 3 jusqu'à la valeur renvoyée par la fonction getrlimit() para RLIMIT_NOFILE .
  2. Réinitialiser tous les gestionnaires de signaux à leur valeur par défaut. Ceci est mieux fait en itérant à travers les signaux disponibles jusqu'à la limite de _NSIG et de les réinitialiser à SIG_DFL .
  3. Réinitialiser le masque de signal en utilisant sigprocmask() .
  4. Assainir le bloc d'environnement, en supprimant ou en réinitialisant les variables d'environnement qui pourraient avoir un impact négatif sur l'exécution du daemon.
  5. Appelez fork() pour créer un processus d'arrière-plan.
  6. Dans l'enfant, appelez setsid() de se détacher de n'importe quel terminal et de créer une entreprise indépendante. session .
  7. Dans l'enfant, appelez fork() à nouveau, afin de s'assurer que le démon ne puisse plus jamais réacquérir un terminal.
  8. Appelez exit() dans le premier enfant, de sorte que seul le deuxième enfant (le processus daemon actuel) reste en place. Cela permet de s'assurer que le processus du daemon est re-parenté à init/PID 1, comme tous les daemons devraient l'être.
  9. Dans le processus du démon, connectez-vous /dev/null à la norme entrée , sortie y erreur .
  10. Dans le processus du démon, réinitialisez l'option umask à 0, de sorte que les modes de fichiers transmis à open() , mkdir() et autres contrôlent directement le mode d'accès aux fichiers et répertoires créés.
  11. Dans le processus du démon, changement le répertoire actuel vers le répertoire racine ( / ), afin d'éviter que le démon ne bloque involontairement le démontage des points de montage.
  12. Dans le processus du daemon, écrivez le daemon PID (tel que renvoyé par getpid() ) dans un fichier PID, par exemple /run/foobar.pid (pour un daemon hypothétique "foobar") pour s'assurer que le daemon ne peut pas être démarré plus d'une fois. Ceci doit être implémenté sans course de façon à ce que le fichier PID ne soit mis à jour que lorsqu'il est vérifié au même moment 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, abandonnez les privilèges, si cela est possible et applicable.
  14. Depuis le processus démon, notifier au processus original lancé que l'initialisation est terminée. Ceci peut être implémenté via un tuyau sans nom ou un canal de communication similaire qui est créé avant le premier processus de fork() et donc disponible à la fois dans le processus original et dans le processus démon.
  15. Appelez exit() dans le processus original. Le processus qui a invoqué le démon doit pouvoir compter sur le fait que cet élément d'information est disponible. exit() se produit après l'initialisation est terminée et tous les canaux de communication externes sont établis et accessibles.

Notez cet avertissement :

Le BSD daemon() fonction ne devrait pas peut être utilisé, car il ne met en œuvre qu'une sous-ensemble de ces étapes.

Un démon qui doit fournir compatibilité avec les systèmes SysV doivent mettre en œuvre le schéma indiqué ci-dessus. Cependant, il est recommandé de rendre ce comportement optionnel 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 POSIX conforme.


Les démons du nouveau style

Pour les démons de type nouveau, l'option les étapes suivantes sont recommandés :

  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, si cela s'applique.
  3. Fournir un code de sortie correct du processus principal du démon, 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 qu'il est défini dans le document Recommandations de la LSB pour les scripts d'init de SysV .
  4. Si cela est possible et applicable, exposez l'interface de contrôle du démon via l'option D-Bus IPC et saisir un nom de bus comme dernière étape de l'initialisation.
  5. Pour l'intégration dans systemd, fournissez un .service unité qui contient des informations sur le démarrage, l'arrêt et la maintenance du démon. Voir systemd.service(5) pour les détails.
  6. Autant que possible, utilisez les fonctionnalités du système d'initialisation pour limiter l'accès du démon aux fichiers, services et autres ressources, c'est-à-dire, dans le cas de systemd, utilisez la fonction contrôle de la limitation des ressources au lieu d'implémenter le vôtre, faites confiance à la fonction de gestion de l'information de systemd suppression des privilèges au lieu de l'implémenter dans le démon, etc. Voir systemd.exec(5) pour les contrôles disponibles.
  7. Si D-Bus est utilisée, rendez votre démon activable par le bus en fournissant une activation de service D-Bus fichier de configuration . Cela présente de multiples avantages : votre démon peut être lancé paresseusement à la demande ; il peut être lancé parallèlement à d'autres démons qui le requièrent - ce qui maximise la parallélisation et l'efficacité. vitesse de démarrage votre démon peut être redémarré en cas d'échec sans perdre les requêtes du bus, car le bus met en attente les requêtes des services activables. Voir en dessous de pour les détails.
  8. Si votre démon fournit des services à d'autres processus locaux ou à des clients distants par l'intermédiaire d'une socket, il doit être fait activable par la prise de courant suivant le schéma indiqué en dessous de . 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 en dessous de pour les 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 l'état via la commande sd_notify(3) interface.
  10. Au lieu d'utiliser le syslog() afin d'établir un journal directement dans le service syslog du système, un démon d'un nouveau genre peut choisir d'établir un journal dans l'erreur standard via fprintf() qui est ensuite transmis à syslog par le système init. Si les niveaux de journal sont nécessaires, ils peuvent être encodés en préfixant les lignes de journal individuelles avec des chaînes comme "<4>" (pour le niveau de journal 4 "WARNING" dans le schéma de priorité de syslog), suivant un style similaire à celui du noyau Linux printk() système de niveau. Pour plus de détails, voir sd-daemon(3) y systemd.exec(5) .

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

11voto

ChuckCottrill Points 2115

Vous ne pouvez pas créer un processus dans linux qui ne peut pas être tué. L'utilisateur Root (uid=0) peut envoyer un signal à un processus, et il y a deux signaux qui ne peuvent pas être attrapés, SIGKILL=9, SIGSTOP=19. Et d'autres signaux (lorsqu'ils ne sont pas attrapés) peuvent également entraîner la fin du processus.

Vous pouvez vouloir une fonction daemonize plus générale, où vous pouvez spécifier un nom pour votre programme/daemon, et un chemin pour exécuter votre programme (peut-être "/" ou "/tmp"). Vous pouvez également vouloir fournir un ou plusieurs fichiers pour stderr et stdout (et éventuellement un chemin de contrôle en utilisant stdin).

Voici les éléments nécessaires :

#include <stdio.h>    //printf(3)
#include <stdlib.h>   //exit(3)
#include <unistd.h>   //fork(3), chdir(3), sysconf(3)
#include <signal.h>   //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h>   //syslog(3), openlog(3), closelog(3)

Et voici une fonction plus générale,

int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
    if(!path) { path="/"; }
    if(!name) { name="medaemon"; }
    if(!infile) { infile="/dev/null"; }
    if(!outfile) { outfile="/dev/null"; }
    if(!errfile) { errfile="/dev/null"; }
    //printf("%s %s %s %s\n",name,path,outfile,infile);
    pid_t child;
    //fork, detach from process group leader
    if( (child=fork())<0 ) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if (child>0) { //parent
        exit(EXIT_SUCCESS);
    }
    if( setsid()<0 ) { //failed to become session leader
        fprintf(stderr,"error: failed setsid\n");
        exit(EXIT_FAILURE);
    }

    //catch/ignore signals
    signal(SIGCHLD,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    //fork second time
    if ( (child=fork())<0) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if( child>0 ) { //parent
        exit(EXIT_SUCCESS);
    }

    //new file permissions
    umask(0);
    //change to path directory
    chdir(path);

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

    //reopen stdin, stdout, stderr
    stdin=fopen(infile,"r");   //fd=0
    stdout=fopen(outfile,"w+");  //fd=1
    stderr=fopen(errfile,"w+");  //fd=2

    //open syslog
    openlog(name,LOG_PID,LOG_DAEMON);
    return(0);
}

Voici un exemple de programme, qui devient un démon, reste en place, puis s'en va.

int
main()
{
    int res;
    int ttl=120;
    int delay=5;
    if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
        fprintf(stderr,"error: daemonize failed\n");
        exit(EXIT_FAILURE);
    }
    while( ttl>0 ) {
        //daemon code here
        syslog(LOG_NOTICE,"daemon ttl %d",ttl);
        sleep(delay);
        ttl-=delay;
    }
    syslog(LOG_NOTICE,"daemon ttl expired");
    closelog();
    return(EXIT_SUCCESS);
}

Notez que SIG_IGN indique qu'il faut attraper et ignorer le signal. Vous pouvez construire un gestionnaire de signal qui peut enregistrer la réception du signal, et définir des drapeaux (comme un drapeau pour indiquer un arrêt en douceur).

9voto

weiyin Points 547

Essayez d'utiliser le daemon fonction :

#include <unistd.h>

int daemon(int nochdir, int noclose);

Desde el page de manuel :

La fonction daemon() est destinée aux programmes qui souhaitent se détacher du terminal de contrôle et s'exécuter en arrière-plan comme des démons système.

Si nochdir est égal à zéro, daemon() change le répertoire de travail du processus du processus appelant vers le répertoire racine ("/") ; sinon, le répertoire de travail de travail actuel est laissé inchangé.

Si noclose est égal à zéro, daemon() redirige l'entrée standard, l'entrée standard standard et l'erreur standard vers /dev/null ; sinon, aucune modification n'est aucun changement n'est apporté à ces descripteurs de fichiers.

6voto

Edwin Buck Points 33097

Je peux m'arrêter à la première exigence "Un démon qui ne peut être arrêté ..."

Ce n'est pas possible mon ami ; cependant, vous pouvez réaliser la même chose avec un bien meilleur outil, un module de noyau.

http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring

Tous les démons peuvent être arrêtés. Certains sont plus faciles à arrêter que d'autres. Même un couple de démons dont le partenaire est maintenu enfoncé, ce qui fait resurgir le partenaire s'il est perdu, peut être arrêté. Il suffit de travailler un peu plus dur pour y parvenir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X