Comment créer un démon en Python?
la recherche sur Google révèle des bribes de code x2. Le premier résultat est à cette recette de code qui a beaucoup de documentation et d'explication, avec quelques discussions utiles ci-dessous.
Toutefois, un autre exemple de code , tout en ne contenant pas tellement de la documentation, inclut un exemple de code pour passer des commandes telles que démarrer, arrêter et redémarrer. Il crée également un fichier PID qui peut être pratique pour vérifier si le démon est déjà en cours d'exécution, etc.
ces deux exemples expliquent comment créer le démon. Il a des choses qui doivent être pris en considération? Un échantillon est-il meilleur que l'autre, et pourquoi?
15 réponses
solution
Une référence de mise en œuvre de PEP 3143 (Standard du processus démon de la bibliothèque) est désormais disponible en tant que python-daemon .
réponse historique
Sander's code sample est supérieur à l'original, qui a été affiché à l'origine en 2004. J'ai un jour contribué un daemonizer pour Pyro, mais j'utiliserais probablement le code de Sander si je devais le refaire.
Il y a de nombreux délicat choses de prendre soin de quand il devient bien éduqués " démon :
-
empêcher les dumps de noyau (de nombreux démons fonctionnent en tant que racine, et les dumps de noyau peuvent contenir des informations sensibles) "
-
se comporter correctement à l'intérieur d'un
chroot
prison -
ensemble UID, GID, répertoire de travail, umask, et d'autres paramètres du processus de manière appropriée pour le cas d'utilisation
-
abandon élevé
suid
,sgid
privilèges -
fermer tous les descripteurs de fichier ouvert, avec des exclusions selon le cas d'utilisation
-
se comporter correctement s'il est démarré dans un contexte déjà détaché, tel que
init
,inetd
, etc. -
mettre en place des manipulateurs de signaux pour un comportement sensible du démon, mais aussi avec des manipulateurs spécifiques déterminés par le cas d'utilisation
-
rediriger les flux standard
stdin
,stdout
,stderr
depuis qu'un processus de démon n'a plus de terminal de contrôle -
gérer un fichier PID comme une serrure coopérative de conseil, qui est une boîte entière de vers en elle-même avec de nombreuses façons contradictoires mais valables de se comporter
-
permettre un nettoyage approprié lorsque le processus est terminé
-
devient en fait un processus de démon sans conduire à zombies
certains D'entre eux sont standard , comme décrit dans la littérature canonique Unix ( avancé Programmation dans l'Environnement UNIX , à la fin de W. Richard Stevens, Addison-Wesley, 1992). D'autres , comme la redirection de flux et traitement de fichier PID , sont comportement conventionnel la plupart des utilisateurs de démon s'attendrait, mais qui sont moins standardisés.
tous ces produits sont couverts par la spécification PEP 3143 " Standard daemon process library " . Le python-daemon travaux d'implémentation de référence sur Python 2.7 ou plus tard, et Python 3.2 ou plus tard.
Voici mon daemon 'Howdy World' de base en Python avec lequel je commence, quand je développe une nouvelle application de daemon.
#!/usr/bin/python
import time
from daemon import runner
class App():
def __init__(self):
self.stdin_path = '/dev/null'
self.stdout_path = '/dev/tty'
self.stderr_path = '/dev/tty'
self.pidfile_path = '/tmp/foo.pid'
self.pidfile_timeout = 5
def run(self):
while True:
print("Howdy! Gig'em! Whoop!")
time.sleep(10)
app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()
notez que vous aurez besoin de la bibliothèque python-daemon
. Vous pouvez l'installer par:
pip install python-daemon
commence par ./howdy.py start
et arrête avec ./howdy.py stop
.
Notez le paquet python-daemon qui résout beaucoup de problèmes derrière les démons sortis de la boîte.
parmi D'autres fonctionnalités qu'il permet (à partir de la description du paquet Debian):
- détacher le procédé dans son propre groupe de procédés.
- Ensemble des processus de l'environnement approprié pour l'exécution de l'intérieur du chroot.
- renonce aux privilèges suid et sgid.
- Fermer tous les descripteurs de fichiers ouverts.
- Changer le répertoire de travail, uid, gid, et umask.
- régler les manipulateurs de signaux appropriés.
- ouvrir nouveaux descripteurs de fichiers pour stdin, stdout, et stderr.
- gère un fichier de verrouillage PID spécifié.
- enregistrer les fonctions de nettoyage pour le traitement à la sortie.
une alternative -- créer un programme Python normal, non-daemonisé puis extérieurement le daemoniser en utilisant supervisord . Cela peut sauver beaucoup de maux de tête, et est *nix - et la langue-portable.
Probablement pas une réponse directe à la question, mais systemd peut être utilisé pour exécuter votre application en tant que démon. Voici un exemple:
[Unit]
Description=Python daemon
After=syslog.target
After=network.target
[Service]
Type=simple
User=<run as user>
Group=<run as group group>
ExecStart=/usr/bin/python <python script home>/script.py
# Give the script some time to startup
TimeoutSec=300
[Install]
WantedBy=multi-user.target
je préfère cette méthode parce que beaucoup de travail est fait pour vous, et puis votre script démon se comporte de la même façon que le reste de votre système.
- Orby
depuis python-daemon n'a pas encore pris en charge python 3.x, et à partir de ce qu'on peut lire sur la liste de diffusion, il peut le sera jamais, j'ai écrit une nouvelle mise en œuvre des PEP 3143: pep3143daemon
pep3143daemon devrait supporter au moins python 2.6, 2.7 et 3.x
contient aussi une classe PidFile.
la bibliothèque ne dépend que de la bibliothèque standard et du module six.
Il peut être utilisé en remplacement de python-démon.
Voici la documentation .
je crains que le module de démon mentionné par @Dustin ne fonctionne pas pour moi. J'ai plutôt installé python-daemon et utilisé le code suivant:
# filename myDaemon.py
import sys
import daemon
sys.path.append('/home/ubuntu/samplemodule') # till __init__.py
from samplemodule import moduleclass
with daemon.DaemonContext():
moduleclass.do_running() # I have do_running() function and whatever I was doing in __main__() in module.py I copied in it.
courir est facile
> python myDaemon.py
juste pour l'exhaustivité voici samplemodule directory content
>ls samplemodule
__init__.py __init__.pyc moduleclass.py
le contenu de moduleclass.py peut être
class moduleclass():
...
def do_running():
m = moduleclass()
# do whatever daemon is required to do.
cette fonction transformera une application en démon:
import sys
import os
def daemonize():
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as err:
sys.stderr.write('_Fork #1 failed: {0}\n'.format(err))
sys.exit(1)
# decouple from parent environment
os.chdir('/')
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as err:
sys.stderr.write('_Fork #2 failed: {0}\n'.format(err))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(os.devnull, 'r')
so = open(os.devnull, 'w')
se = open(os.devnull, 'w')
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
une chose de plus à penser quand on daemonise en python:
Si vous utilisez python journalisation et que vous souhaitez continuer à l'utiliser après daemonizing, assurez-vous d'appeler close()
sur les gestionnaires (en particulier les gestionnaires de fichiers).
si vous ne le faites pas, le gestionnaire peut toujours penser qu'il a des fichiers ouverts, et vos messages disparaîtront tout simplement - en d'autres termes, assurez-vous que le logger sait que ses fichiers sont fermés!
cela suppose que lorsque vous daemonisez vous fermez tous les descripteurs de fichiers ouverts sans distinction - au lieu de cela vous pouvez essayer de fermer tous les fichiers sauf les logs (mais il est généralement plus simple de fermer tous puis de rouvrir ceux que vous voulez).
j'ai modifié quelques lignes dans l'échantillon de code de Sander Marechal (mentionné par @JeffBauer dans la réponse acceptée ) pour ajouter une méthode quit()
qui est exécutée avant que le démon ne soit arrêté. C'est parfois très utile.
Note: je n'utilise pas le module" Python-daemon " car la documentation est toujours manquante (voir aussi beaucoup D'autres questions SO) et est plutôt obscure (Comment démarrer/arrêter correctement un démon de la ligne de commande avec ce module?)
après quelques années et de nombreuses tentatives, maintenant je me rends compte qu'il y a une meilleure façon que de vouloir démarrer, arrêter, redémarrer un démon directement à partir de Python: utilisez les outils OS à la place!
en bref, au lieu de faire python myapp start
et python myapp stop
, je fais ceci pour démarrer l'application:
screen -S myapp python myapp.py
CTRL+A, D to detach
ou screen -dmS myapp python myapp.py
à de début et de le détacher en une seule commande .
puis:
screen -r myapp
à attacher à nouveau à ce terminal. Une fois dans le terminal, il est possible d'utiliser CTRL+C pour l'arrêter.
la manière la plus simple de créer un démon avec Python est d'utiliser le Twisted framework piloté par l'événement. Il gère tout ce qui est nécessaire à la démonisation pour vous. Il utilise le "Reactor Pattern pour traiter les requêtes concurrentes.
80% du temps, quand les gens disent "démon", ils veulent seulement un serveur. Comme la question n'est pas claire sur ce point, il est difficile de dire quel pourrait être le domaine des réponses. Comme un serveur est adéquat, commencez par là. Si un "démon" réel est réellement nécessaire (c'est rare), lisez nohup
comme un moyen de démoniser un serveur.
Jusqu'à ce qu'un démon réel soit réellement requis, il suffit d'écrire un serveur simple.
Regardez aussi la mise en œuvre de WSGI reference .
regardez aussi le Simple serveur HTTP .
" y a-t-il d'autres éléments à prendre en considération? " Oui. Environ un million de choses. Ce protocole? Combien de demandes? Combien de temps pour répondre à chaque demande? Combien de fois vont-ils arriver? Allez-vous utiliser un processus dédié? Les Threads? Les sous-processus? Écrire un démon est un gros travail.