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 modifications sont détectées, il doit écrire le chemin d'accès à la console où il a été démarré plus un retour à la ligne.
J'ai déjà le système de fichiers changeant le code presque prêt mais je ne peux pas comprendre comment créer un démon.
Mon code est d'ici: http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html
Que faire après la fourche?
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;
}
8 réponses
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 seraient détectés, il devrait écrire le chemin d'accès à la console où il a été démarré + un retour à la 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 enregistrer les messages dans les fichiers (debug, erreur,...).
En plus de cela, il y a quelques étapes requises {[15] } pour démoniser un processus.
Si je me souviens bien, ces étapes sont:
- fork désactivez le processus parent et laissez-le se terminer si le forking a réussi. - >Parce que le processus parent est terminé, le processus enfant s'exécute maintenant en arrière-plan.
- setsid - crée une nouvelle session. Le processus appelant devient le leader de la nouvelle session et du processus chef de groupe du nouveau groupe de processus. Le processus est maintenant détaché de son terminal de contrôle (CTTY).
- signaux de capture - 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 responsables de session peuvent obtenir un ATS à nouveau.)
- chdir - Changer le répertoire de travail du démon.
- umask - changer le masque de mode de fichier en fonction de les besoins du démon.
- close - 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 code squelette qui montre les étapes de base:
/*
* 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;
}
- Compiler le code:
gcc -o firstdaemon daemonize.c
- Démarrer le démon:
./firstdaemon
Vérifier si tout fonctionne correctement:
ps -xj | grep firstdaemon
La sortie devrait être similaire à ceci un:
+------+------+------+------+-----+-------+------+------+------+-----+ | 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 la suivante:
- , Le démon n'a pas de terminal de contrôle (ATS = ?)
- L'ID du processus parent (PPID) est 1 (le processus init)
- le PID != SID {[27] } ce qui signifie que notre processus N'est pas le leader de session
(à cause de la deuxième fourchette ()) - parce que PID != SID notre processus ne peut pas prendre le contrôle d'un TTY encore
Lire le syslog:
- Recherchez votre fichier syslog. Le mien est ici:
/var/log/syslog
Faire un:
grep firstdaemon /var/log/syslog
-
La sortie doit être similaire à celle-ci:
firstdaemon[3387]: First daemon started. firstdaemon[3387]: First daemon terminated.
Une remarque:
En réalité, vous voudriez également implémenter un gestionnaire de signal et configurer la journalisation correctement (fichiers, niveaux de journal...).
Pour en savoir Plus:
man 7 daemon
décrit comment créer démon dans les moindres détails. Ma réponse est juste extrait de ce manuel.
, Il existe au moins deux types de démons:
- traditionnel SysV démons (, vieux style),
- systemd démons (nouveaux).
Démons SysV
Si vous êtes intéressé par le démon SysV traditionnel, vous devez implémenter le suivant les étapes:
- ferme tous les descripteurs de fichiers ouverts sauf 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, ceci est mieux implémenté en itérant à travers
/proc/self/fd
, avec un repli de l'itération du descripteur de fichier 3 à la valeur renvoyée pargetrlimit()
pourRLIMIT_NOFILE
.- Réinitialiser tous gestionnaires de signal à leur défaut. Ceci est mieux fait par itération sur les signaux disponibles jusqu'à la limite de
_NSIG
et réinitialisantSIG_DFL
.- Réinitialiser le masque de signal en utilisant
sigprocmask()
.- 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 démon.
- Appeler
fork()
, pour créer un processus d'arrière-plan.- dans l'enfant, appelez
setsid()
pour se détacher de tout terminal et créez une session indépendante .- dans l'enfant, appelez
fork()
encore une fois, pour s'assurer que le démon ne peut jamais ré-acquérir un terminal à nouveau.- Appeler
exit()
dans le premier enfant, de sorte que seul le deuxième enfant (le démon) reste autour. Cela garantit que le processus démon est re-parent à init/PID 1, comme tous les démons devraient l'être.- dans le processus démon, connectez-vous
/dev/null
à la norme entrée , sortie, et erreur.- dans le processus démon, réinitialisez le
umask
à 0, de sorte que les modes de fichier transmis àopen()
,mkdir()
et telcomme contrôler directement le mode d'accès des fichiers et répertoires créés.- dans le processus démon, change le répertoire courant vers le répertoire racine (
/
), afin d'éviter que le démon bloque involontairement le démontage des points de montage.- dans le processus de démon, écrire le démon PID (retourné par
getpid()
) pour un fichier PID, par exemple/run/foobar.pid
(pour un hypothétique démon "foobar") pour s'assurer que le démon ne peut pas être démarré plusieurs fois. Cela doit être mis en œuvre 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.- dans le processus démon, supprimez les privilèges, si possible et applicable.
- à partir du processus démon, informez le processus d'origine démarré que l'initialisation est terminée. Cela peut être mis en œuvre via un tuyau sans nom ou un canal de communication similaire créé avant le premier
fork()
et donc disponible à la fois dans le processus original et dans le processus démon.- Appeler
exit()
dans le processus d'origine. Le processus qui a invoqué le démon doit pouvoir compter sur celaexit()
ça arrive après l'initialisation est terminée et tous les canaux de communication sont établis et accessibles.
Notez cet avertissement:
Le BSD
daemon()
la fonction ne doit pas être utilisée, car elle n'implémente qu'un sous-ensemble de ces étapes.Un démon qui doit fournir compatibilité avec les systèmes SysV devrait implémenter le schéma indiqué ci-dessus. Toutefois, il est recommandé de faire cette 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 POSIX conforme.
Nouveaux Démons De Style
Pour les démons de style nouveau, Les étapes suivantes sont recommandées:
- si {[23] } est reçu, arrêtez le démon et quittez proprement.
- Si {[24] } est reçu, rechargez le fichiers de configuration, si cela s'applique.
- fournir un code de sortie correct du processus démon principal, car il est utilisé par le système d'initialisation 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 .
- 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.
- pour intégration dans systemd, fournir un .service unité fichier 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.- autant que possible, fiez-vous aux 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, fiez-vous au contrôle de limite de ressources de systemd au lieu d'implémenter le vôtre, fiez-vous au contrôle de privilège laissant tomber le code au lieu de l'implémenter dans le démon, et similaire. Tu vois
systemd.exec(5)
pour les commandes disponibles.- si D-Bus est utilisé, rendez votre démon activable par bus en fournissant un fichier de configuration d'activation du 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 le nécessitent - 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 de requêtes de bus, car le bus met en file d'attente des demandes de services activables. Voir ci-dessous pour plus de détails.
- si votre démon fournit des services à d'autres processus locaux ou clients distants via un socket, il doit être activable par socket en suivant le schéma indiqué ci-dessous. Comme l'activation de D-Bus, cela permet le démarrage à la demande des services ainsi qu'une parallélisation améliorée du service start-up. En outre, pour les protocoles sans état (tels que syslog, DNS), un démon implémentant une activation basée sur socket peut être redémarré sans perdre une seule requête. Voir ci-dessous pour plus de détails.
- le cas échéant, un démon doit informer le système d'initialisation de l'achèvement du démarrage ou des mises à jour
sd_notify(3)
interface.- au lieu d'utiliser le
syslog()
appel à se connecter directement au service syslog système, un démon de style nouveau peut choisir de se connecter simplement à erreur standard viafprintf()
, qui est ensuite transmis à syslog par le système d'initialisation. Si des niveaux de log sont nécessaires, ceux-ci peuvent être codés en préfixant des lignes de log individuelles avec des chaînes comme " " (pour le niveau de log 4 "WARNING" dans le schéma de priorité syslog), suivant un style similaire à celui du noyau Linuxprintk()
système de niveau. Pour plus de détails, voirsd-daemon(3)
etsystemd.exec(5)
.
Pour en savoir plus lire l'man 7 daemon
.
Je peux m'arrêter à la première exigence "un démon qui ne peut pas être arrêté ..."
Pas possible mon ami; cependant, vous pouvez réaliser la même chose avec un bien meilleur outil, un module noyau.
Http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring
Tous les démons peuvent être arrêtés. Certains sont plus facilement arrêtés que d'autres. Même une paire de démons avec le partenaire dans hold down, respawning le partenaire si perdu, peut être arrêté. Vous avez juste à travailler un peu plus dur à elle.
Vous ne pouvez pas créer un processus sous 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 interceptés, SIGKILL=9, SIGSTOP=19. Et d'autres signaux (lorsqu'ils ne sont pas interceptés) peuvent également entraîner la fin du processus.
, Vous voudrez peut-être un plus générale daemonize fonction, où vous pouvez spécifier un nom pour votre programme/démon, et un chemin pour exécuter votre programme (peut-être "/" ou "/tmp"). Vous pouvez également fournir des fichiers pour stderr et stdout (et éventuellement un chemin de contrôle en utilisant stdin).
Voici le nécessaire comprend:
#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, se bloque, puis part.
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 d'attraper et d'ignorer le signal. Vous pouvez créer un gestionnaire de signal qui peut enregistrer la réception du signal et définir des indicateurs (comme un indicateur pour indiquer un arrêt gracieux).
Essayez d'utiliser la fonction daemon
:
#include <unistd.h>
int daemon(int nochdir, int noclose);
La fonction daemon () est pour les programmes qui souhaitent se détacher à partir du terminal de contrôle et exécuter en arrière-plan en tant que système démon.
Si nochdir vaut zéro, daemon () change le courant du processus appelant répertoire de travail dans le répertoire racine ("/"); sinon, le courant le répertoire de travail reste inchangé.
Si noclose vaut zéro, daemon () redirige entrée standard, standard sortie et erreur standard dans / dev / null; sinon, aucune modification n'est ces descripteurs de fichiers.
Si votre application est l'une des:
{
".sh": "bash",
".py": "python",
".rb": "ruby",
".coffee" : "coffee",
".php": "php",
".pl" : "perl",
".js" : "node"
}
Et cela ne vous dérange pas une dépendance NodeJS puis installez NodeJS et ensuite:
npm install -g pm2
pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above
pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores
pm2 list
Pour garder toutes les applications en cours d'exécution au redémarrage (et daemonise pm2):
pm2 startup
pm2 save
Maintenant, vous pouvez:
service pm2 stop|restart|start|status
(vous permet également de surveiller facilement les changements de code dans le répertoire de votre application et de redémarrer automatiquement le processus de l'application lorsqu'un changement de code se produit)
En appelant fork (), vous avez créé un processus enfant. Si le fork est réussi (fork a renvoyé un PID non nul), l'exécution se poursuivra à partir de ce point à partir du processus enfant. Dans ce cas, nous voulons sortir gracieusement du processus parent, puis continuer notre travail dans le processus enfant.
Peut-être que cela aidera: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html
Un démon est juste un processus en arrière-plan. Si vous voulez démarrer votre programme lorsque le système d'exploitation démarre, sous linux, vous ajoutez votre commande start à /etc / rc.d / rc.local (exécuté après tous les autres scripts) ou /etc/startup.sh
Sous windows, vous créez un service, enregistrez le service, puis définissez-le pour démarrer automatiquement au démarrage dans le panneau administration - > Services.