Как вы создаете демон в Python?

244

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

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

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

davidmytton
источник
1
Я всегда находил код демонизации ненужным. Почему бы просто не позволить оболочке сделать это?
emil.p.stanchev
17
Потому что это не делает setsid или setpgrp.
Bmargulies
4
Используйте supervisord.org . Таким образом, вам не нужно выполнять fork () или перенаправлять ваш stdin / stderr. Просто напишите нормальную программу.
Гетли

Ответы:

169

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

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

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

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

Джефф Бауэр
источник
72
Изменить: так как я первоначально разместил этот ответ, теперь доступна справочная реализация PEP 3143: pypi.python.org/pypi/python-daemon
Джефф Бауэр
@JeffBauer Первоначальная ссылка умерла, я помню, что она была полезна, вы случайно не узнали бы живую ссылку для этого?
CrazyCasta
1
@CrazyCasta: версия Сандера Марешала все еще доступна на Wayback Machine
Джефф Бауэр
1
@JeffBauer: код Сандера все еще лучше чем http://pypi.python.org/pypi/python-daemon. Более надежный. Только один пример: попробуйте запустить один и тот же демон два разаpython-daemon : большая ужасная ошибка. С кодом Сандера: приятное замечание «Демон уже работает».
Basj
2
Поскольку документация по модулю "python-daemon" по-прежнему отсутствует (см. Также много других вопросов по SO) и довольно неясна (как правильно запустить / остановить демон из командной строки с этим модулем?), Я изменил пример кода Сандера Марешала, чтобы добавить quit()метод, который выполняется до остановки демона. Вот.
Basj
163

Есть много интересных вещей, о которых нужно позаботиться, когда вы станете хорошим демоном :

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

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

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

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

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

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

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

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

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

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

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

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

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

большой нос
источник
26
«Gaol» написано правильно, потому что именно так его написал В. Ричард Стивенс :-)
bignose
7
Gaol это английская вещь . Плакат из Австралии, так что это имеет смысл.
Девин
1
Есть ли планы по созданию дружественной для py3k версии?
Тим Тисдалл
97

Вот мой базовый демон 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.

Дастин Киркланд
источник
5
Этот daemonмодуль, который вы импортируете, не является стандартной частью Python (пока). Это должно быть установлено с pip install python-daemonили эквивалентным.
Ноябрь
6
Я установил python-daemon, как вы описали, но когда я пытаюсь запустить свое приложение (так же, как ваши последние 3 строки), я получаю ImportError: не могу импортировать имя бегуна
Nostradamnit
Можете ли вы проверить, правильно ли он установлен? $ dpkg -L python-daemon | grep runner /usr/share/pyshared/daemon/runner.py
Дастин Киркланд
4
Это предложение кажется устаревшим - по крайней мере, по состоянию на сентябрь 2013 года python.org/dev/peps/pep-3143 не упоминает «бегуна», которого можно импортировать. Это, конечно, объяснит наблюдение @ Nostradamnit.
offby1
2
Это все еще работает для меня, в сентябре 2013 года, на Ubuntu 13.04, с установленными пакетами Python, python2.7 и python-daemon. Однако в python3 я вижу ошибку «от бегуна импорта демона ImportError: нет модуля с именем daemon»
Дастин Киркланд,
42

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

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

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

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

Крис Джонсон
источник
1
Я думаю, что это лучший способ. Особенно, если вы хотите запустить несколько демонов в одной операционной системе. Не кодируйте, используйте повторно.
Гетли
Это упрощает много вопросов. Я написал настоящие демоны - они не легки.
Крис Джонсон
1
Лучший ответ здесь спрятан :)
kawing-chiu
1
Это золото. Потратив часы на попытки запустить Python-Daemon, это готовое решение, которое работает для меня. Отличная документация и примеры позволили моему демону заработать за несколько минут.
Нихил Саху
17

Возможно, это не прямой ответ на вопрос, но 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

Люк Дюпен
источник
Это правильный и вменяемый способ. 1) Необходимо сохранить в /etc/systemd/system/control.service 2) Управляемый sudosystemctl start control.service
jimper
7

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

Сергей Р
источник
6

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

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

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

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

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

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

Стефан Шульхен
источник
6

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

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())
Иван Колесников
источник
5

Боюсь, модуль демона, упомянутый @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.
Somum
источник
2

Еще одна вещь, о которой стоит подумать при демонизации в python:

Если вы используете ведение журнала Python и хотите продолжать использовать его после демонизации, обязательно вызовите close()обработчики (особенно обработчики файлов).

Если вы этого не сделаете, обработчик все еще может думать, что у него открыты файлы, и ваши сообщения просто исчезнут - другими словами, убедитесь, что регистратор знает, что его файлы закрыты!

Это предполагает, что когда вы демонизируете, вы закрываете ВСЕ дескрипторы открытого файла без разбора - вместо этого вы можете попробовать закрыть все, кроме файлов журналов (но обычно проще закрыть все, а затем снова открыть те, которые вы хотите).

Мэтью Уилкоксон
источник
Считаете ли вы, что открытие нового обработчика журналирования лучше, чем передача обработчика журналирования демону с помощью, например, опции DaemonContext files_preserve?
HeyWatchThis
Вы только закрываете регистратор, но не создаете новый (он просто заново открывает его, когда это необходимо). Но даже несмотря на то, что это действительно легко сделать, может быть, лучше использовать DaemonContext, поскольку он, вероятно, делает некоторые другие умные вещи (при условии, что сохранение все еще позволяет надлежащую демонизацию).
Мэтью Уилкоксон
2

Хотя вы можете предпочесть чистое решение Python, предоставляемое модулем python-daemon , в BSD и Linux есть daemon(3)функция, которая, libcпо крайней мере, будет работать правильно.

Вызвать его из python легко:

import ctypes

ctypes.CDLL(None).daemon(0, 0) # Read the man-page for the arguments' meanings

Осталось только создать (и заблокировать) PID-файл. Но с этим ты справишься сам ...

Михаил Т.
источник
1

Я изменил несколько строк в образце кода Сандера Марешала (упомянутого @JeffBauer в принятом ответе ), чтобы добавить quit()метод, который выполняется до остановки демона. Это иногда очень полезно.

Вот.

Примечание: я не использую модуль "python-daemon", потому что документация все еще отсутствует (см. Также много других вопросов SO) и довольно неясна (как правильно запустить / остановить демон из командной строки с этим модулем?)

Basj
источник
-1

После нескольких лет и многих попыток (я попробовал все ответы, приведенные здесь, но все они имели незначительные недостатки в конце), теперь я понимаю, что есть лучший способ, чем запуск, остановка, перезапуск демона непосредственно из Python : используйте вместо этого инструменты ОС.

Например, для Linux вместо того, чтобы делать python myapp startи python myapp stop, я делаю это, чтобы запустить приложение:

screen -S myapp python myapp.py    
CTRL+A, D to detach

или screen -dmS myapp python myapp.pyдля запуска и отсоединить его в одной команде .

Затем:

screen -r myapp

подключить к этому терминалу снова. Оказавшись в терминале, можно использовать CTRL + C, чтобы остановить его.

Basj
источник
-2

Самый простой способ создания демона с помощью Python - это использование Twisted- событийной среды. Он обрабатывает все вещи, необходимые для демонизации для вас. Он использует шаблон Reactor для обработки одновременных запросов.

Трэвис Б. Хартвелл
источник
5
Это слишком большой молоток для использования. Большинство людей просто хотят запустить короткий скрипт на Python, который они написали как демон. Python-daemon, как описано выше, является правильным ответом.
Том Свирли
2
Хотя этот ответ был довольно высокомерным, он был полезен.
Пятница
-28

В 80% случаев, когда люди говорят «демон», им нужен только сервер. Поскольку вопрос в этом вопросе совершенно неясен, трудно сказать, какой может быть область возможных ответов. Поскольку сервер адекватен, начните с него. Если фактический «демон» действительно нужен (это редко), читайтеnohup как способ демонизации сервера.

До тех пор, пока фактический демон фактически не требуется, просто напишите простой сервер.

Также посмотрите на справочную реализацию WSGI .

Также посмотрите на простой HTTP-сервер .

«Есть ли какие-то дополнительные вещи, которые необходимо учитывать?» Да. Около миллиона вещей. Какой протокол? Сколько запросов? Как долго обслуживать каждый запрос? Как часто они будут приезжать? Будете ли вы использовать выделенный процесс? Потоки? Подпроцессы? Написание демона - большая работа.

С. Лотт
источник
12
Ни одна из этих библиотек даже не делает ни одной fork(), не говоря уже о двух. Они не имеют ничего общего с демонизацией.
Брэндон Родс
8
В операционных системах Unix процесс «демона», как, например, дежурные, которые греки называли «демонами», является процессом, «стоящим на стороне». Вместо непосредственного обслуживания одного пользователя через TTY этого пользователя, демон не принадлежит TTY, но может отвечать на запросы от многих пользователей в системе, или, как crondили syslogdвыполняет служебные услуги для всей системы. Чтобы создать процесс демона, необходимо, по крайней мере, выполнить двойную операцию - fork()со всеми закрытыми дескрипторами файлов, так что он неуязвим для сигналов от всех управляющих терминалов, включая системную консоль. Смотри бигнозный ответ.
Брэндон Родс
5
@S Lott - «сервер» описывает, что делает процесс (прослушивает входящие запросы вместо того, чтобы инициировать свои собственные действия); «Демон» описывает , как процесс работает (без окна или управляющий терминал). SimpleHTTPServerэто действительно сервер, но тот, который изначально не знает, как демонизировать себя (вы можете, например, Ctrl-C). nohupэто утилита для демона наивного процесса - так что ваш nohupped сервер действительно как демон , и сервер, точно так , как вы утверждаете. Этот вопрос переполнения стека по существу спрашивал: «Как я могу реализовать nohupв Python?»
Брэндон Роудс
5
Да, это так, но мое понимание вопроса OP заключается в том, что он хочет выполнить демонизацию из своей программы на Python и без использования чего-то еще.
Нуфал Ибрагим
4
@ S Lott - Вы не должны быть впечатлены! Автор любого другого ответа знал, что означает «демон», поэтому моя способность интерпретировать этот вопрос едва ли уникальна. :) А с чего вы взяли, что я хочу, чтобы автор заново изобрел колесо? Я думаю, что nohupэто хороший инструмент, и я уберу свой голос -1, если вы просто перенесете эту полезную идею в свой фактический ответ. На самом деле, если вы упомянете supervisordи как это также избавит автора от необходимости вести журналирование, запускать-останавливать сценарий и перезапускать регулирование, тогда я даже +1 вам. :)
Брэндон Роудс