Как вы создаете демон в Python? - PullRequest
232 голосов
/ 23 января 2009

Поиск в Google показывает x2 фрагменты кода. Первый результат - этот кодовый рецепт , который содержит много документации и объяснений, а также некоторые полезные обсуждения ниже.

Тем не менее, другой пример кода , хотя и не содержит так много документации, содержит пример кода для передачи таких команд, как запуск, останов и перезапуск. Он также создает PID-файл, который может быть полезен для проверки, запущен ли демон и т. Д.

Оба примера объясняют, как создать демона. Есть ли какие-то дополнительные вещи, которые необходимо учитывать? Один образец лучше другого и почему?

Ответы [ 15 ]

161 голосов
/ 27 марта 2009

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * .

  • предотвращение дампов ядра (многие демоны запускаются от имени root, а дампы ядра могут содержать конфиденциальную информацию)

  • ведет себя правильно внутри chroot тюрьмы

  • установить UID, GID, рабочий каталог, umask и другие параметры процесса соответственно для варианта использования

  • отказаться от повышенных suid, sgid привилегий

  • закрыть все дескрипторы открытых файлов, с исключениями в зависимости от варианта использования

  • ведет себя корректно, если запущен в уже отсоединенном контексте, таком как init, inetd и т. Д.

  • настроить обработчики сигналов для разумного поведения демона, но также с определенными обработчиками, определяемыми сценарием использования

  • перенаправить стандартные потоки stdin, stdout, stderr, поскольку у процесса-демона больше нет управляющего терминала

  • обрабатывать PID-файл как совместную консультативную блокировку, которая представляет собой целую банку червей сама по себе со многими противоречивыми, но действительными способами поведения

  • разрешить надлежащую очистку после завершения процесса

  • фактически становится процессом демона, не приводя к зомби

Некоторые из них стандартные , как описано в канонической литературе по Unix ( Расширенное программирование в среде UNIX , покойным У. Ричардом Стивенсом, Addison-Wesley, 1992). Другие, такие как перенаправление потоков и обработка файлов PID , обычное поведение , на которые большинство пользователей демонов ожидают, но они менее стандартизированы.

На все они распространяется спецификация PEP 3143 «Стандартная библиотека процессов демона» . Справочная реализация python-daemon работает на Python 2.7 или более поздней версии и Python 3.2 или более поздней версии.

152 голосов
/ 23 января 2009

Текущее решение

Эталонная реализация PEP 3143 (Стандартная библиотека процессов демона) теперь доступна как python-daemon .

Исторический ответ

Пример кода Сандера Маречала превосходит оригинал, который был первоначально опубликован в 2004 году. Однажды я добавил демонайзер для Pyro, но, вероятно, использовал бы код Сандера, если бы мне пришлось это делать заново.

92 голосов
/ 28 января 2012

Вот мой базовый демон Python 'Howdy World', с которого я начинаю, когда разрабатываю новое приложение-демон.

#!/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()

Обратите внимание, что вам понадобится библиотека python-daemon. Вы можете установить его:

pip install python-daemon

Тогда просто начните с ./howdy.py start и остановите с ./howdy.py stop.

42 голосов
/ 24 марта 2011

Обратите внимание на пакет python-daemon , который решает множество проблем за демонами из коробки.

Среди других возможностей, которые он позволяет (из описания пакета Debian):

  • Отделить процесс от собственной группы процессов.
  • Установить среду процесса, подходящую для работы внутри chroot.
  • Отказаться от привилегий suid и sgid.
  • Закрыть все открытые дескрипторы файлов.
  • Изменить рабочий каталог, uid, gid и umask.
  • Установите соответствующие обработчики сигналов.
  • Открытие новых файловых дескрипторов для stdin, stdout и stderr.
  • Управление указанным файлом блокировки PID.
  • Регистрация функций очистки для обработки на выходе.
28 голосов
/ 23 июня 2013

Альтернатива - создать обычную недемонизированную программу Python, а затем внешне демонизировать ее, используя supervisord . Это может сэкономить много головной боли, и * nix- и переносимо для языка.

12 голосов
/ 19 мая 2015

Возможно, это не прямой ответ на вопрос, но systemd можно использовать для запуска приложения в качестве демона. Вот пример:

[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

Я предпочитаю этот метод, потому что большая часть работы сделана для вас, а затем ваш скрипт-демон работает аналогично остальной части вашей системы.

-Orby

6 голосов
/ 06 декабря 2011

YapDi - это относительно новый модуль Python, который появился в Hacker News. Выглядит довольно полезно, может использоваться для преобразования скрипта Python в режим демона из скрипта.

5 голосов
/ 15 сентября 2014

, поскольку python-daemon еще не поддерживает python 3.x, и из того, что можно прочитать в списке рассылки, он может никогда не будет, я написал новую реализацию PEP 3143: pep3143daemon

pep3143daemon должен поддерживать как минимум Python 2.6, 2.7 и 3.x

Он также содержит класс PidFile.

Библиотека зависит только от стандартной библиотеки и от шести модулей.

Может использоваться как замена для python-демона.

Вот документация .

4 голосов
/ 14 сентября 2016

Эта функция преобразует приложение в демон:

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())
3 голосов
/ 12 сентября 2016

Боюсь, модуль демона, упомянутый @Dustin, у меня не сработал. Вместо этого я установил python-daemon и использовал следующий код:

# 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.

Бег легко

> python myDaemon.py

просто для полноты здесь приведено содержимое каталога samplemodule

>ls samplemodule
__init__.py __init__.pyc moduleclass.py

Содержимое файла moduleclass.py может быть

class moduleclass():
    ...

def do_running():
    m = moduleclass()
    # do whatever daemon is required to do.
...