Как мне реализовать простой кроссплатформенный демон Python? - PullRequest
21 голосов
/ 13 февраля 2010

Я бы хотел, чтобы моя программа на Python запускалась в фоновом режиме как демон, в Windows или Unix. Я вижу, что пакет python-daemon предназначен только для Unix; есть ли альтернатива для кроссплатформенности? Если возможно, я хотел бы сохранить код как можно проще.

Ответы [ 5 ]

10 голосов
/ 13 февраля 2010

В Windows это называется «сервис», и вы можете реализовать его довольно легко, например. с модулем win32serviceutil, часть pywin32 . К сожалению, две «ментальные модели» - service vs daemon - очень отличаются в деталях, даже если они служат схожим целям, и я не знаю ни одного фасада Python, который пытался бы объединить их в единую структуру.

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

Этому вопросу 6 лет, но у меня была та же проблема, и существующие ответы не были кроссплатформенными для моего варианта использования. Хотя службы Windows часто используются так же, как и демоны Unix, в конце концов они существенно различаются, и «в деталях дьявол». Короче говоря, я решил попытаться найти что-то, что позволило бы мне запускать один и тот же код приложения как в Unix, так и в Windows, в то же время выполняя ожидания для хорошо работающего демона Unix (который лучше объяснить в других местах ) как можно лучше на обеих платформах:

  1. Закрытие открытых дескрипторов файлов (как правило, все они, но некоторым приложениям может потребоваться защитить некоторые дескрипторы от закрытия)
  2. Измените рабочий каталог для процесса на подходящее место, чтобы предотвратить ошибки "Directory Busy"
  3. Изменить маску создания доступа к файлу (os.umask в мире Python)
  4. Переместить приложение в фоновый режим и отключить его от исходного процесса
  5. Полностью развестись с терминалом, включая перенаправление STDIN, STDOUT и STDERR на разные потоки (часто DEVNULL), и предотвратить повторный захват управляющего терминала
  6. Обработка сигналов, в частности, SIGTERM.

Основная проблема кросс-платформенной демонизации заключается в том, что Windows как операционная система действительно не поддерживает понятие демона: приложения, которые запускаются из терминала (или в любом другом интерактивном контексте, включая запуск из Explorer, и т. д.) будет продолжать работать с видимым окном, если только управляющее приложение (в данном примере Python) не имеет GUI без окон. Кроме того, обработка сигналов Windows крайне неадекватна, и попытки отправить сигналы в независимый процесс Python (в отличие от подпроцесса, который не выдержит закрытия терминала) почти всегда приводят к немедленному выходу этого Python процесс без очистки (без finally:, без atexit, без __del__ и т. д.).

Службы Windows (хотя во многих случаях это жизнеспособная альтернатива) были в принципе для меня невозможны: они не кроссплатформенные, и для них потребуется модификация кода. pythonw.exe ( версия Python без окон , которая поставляется со всеми последними двоичными файлами Windows Python) ближе, но она все еще не совсем уходит: в частности, она не может улучшить ситуацию с обработкой сигналов и вы все еще не можете легко запустить приложение pythonw.exe из терминала и взаимодействовать с ним во время запуска (например, для доставки аргументов динамического запуска в ваш скрипт, скажем, возможно, пароля, пути к файлу и т. д.), до «демонизация».

В итоге я остановился на использовании subprocess.Popen с ключевым словом creationflags=subprocess.CREATE_NEW_PROCESS_GROUP для создания независимого процесса без окон:

import subprocess

independent_process = subprocess.Popen(
    '/path/to/pythonw.exe /path/to/file.py',
    creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
)

Однако, это все еще оставило меня с дополнительным вызовом при запуске связи и обработке сигналов. Не вдаваясь в тонну деталей, для первого моя стратегия была:

  1. pickle важные части пространства имен процесса запуска
  2. Сохраните это в tempfile
  3. Добавить путь к этому файлу в среде дочернего процесса перед запуском
  4. Извлечение и возврат пространства имен из функции «демонизация»

Для обработки сигналов мне нужно было немного более креативно. В рамках «демонизированного» процесса:

  1. Игнорировать сигналы в процессе демона, поскольку, как уже упоминалось, все они завершают процесс немедленно и без очистки
  2. Создать новый поток для управления обработкой сигналов
  3. Этот поток запускает дочерние процессы обработки сигналов и ожидает их завершения
  4. Внешние приложения отправляют сигналы в дочерний процесс обработки сигналов, вызывая его завершение и завершение
  5. Затем эти процессы используют номер сигнала в качестве кода возврата
  6. Поток обработки сигналов считывает код возврата и затем вызывает либо определяемый пользователем обработчик сигналов, либо использует API cytpes, чтобы вызвать соответствующее исключение в основном потоке Python
  7. Промыть и повторить для новых сигналов

Тем не менее, для всех, кто столкнется с этой проблемой в будущем, я свернул библиотеку под названием daemoniker , которая объединяет правильную демонизацию Unix и вышеуказанной стратегии Windows в единый фасад. кроссплатформенный API выглядит следующим образом:

from daemoniker import Daemonizer

with Daemonizer() as (is_setup, daemonizer):
    if is_setup:
        # This code is run before daemonization.
        do_things_here()

    # We need to explicitly pass resources to the daemon; other variables
    # may not be correct
    is_parent, my_arg1, my_arg2 = daemonizer(
        path_to_pid_file,
        my_arg1,
        my_arg2
    )

    if is_parent:
        # Run code in the parent after daemonization
        parent_only_code()

# We are now daemonized, and the parent just exited.
code_continues_here()
4 голосов
/ 13 февраля 2010

На ум приходят два варианта:

  1. Перенесите вашу программу в службу Windows . Вероятно, вы можете разделить большую часть своего кода между двумя реализациями.

  2. Действительно ли ваша программа использует какие-либо функции демона? Если нет, вы переписываете его как простой сервер, который работает в фоновом режиме , управляет связью через сокеты и выполняет свои задачи. Вероятно, он будет потреблять больше системных ресурсов, чем демон, но это будет зависеть от платформы.

2 голосов
/ 13 февраля 2010

В целом концепция демона специфична для Unix, в частности ожидаемое поведение в отношении масок создания файлов, иерархии процессов и обработки сигналов.

Вы можете найти PEP 3143 полезным, когда предлагается предложение продолжения python-daemon для Python 3.2, а также обсуждаются многие связанные с демонизирующие модули и реализации .

0 голосов
/ 13 февраля 2010

Причина, по которой он работает только в Unix, заключается в том, что daemons являются специфической концепцией Unix, т.е. фоновым процессом, инициируемым операционной системой и обычно выполняющимся как дочерний элемент корневого PID. Windows не имеет прямого эквивалента демону Unix, самый близкий, о котором я могу подумать, - это служба Windows. Для Windows есть программа pythonservice.exe. Не уверен, поддерживается ли он во всех версиях python, хотя

...