Як створити демон в Python?


244

Пошук у Google виявляє фрагменти коду x2. Перший результат - це рецепт коду, який містить багато документації та пояснень, а також корисну дискусію під ним.

Однак інший зразок коду , хоча він не містить стільки документації, включає зразок коду для передачі команд, таких як пуск, зупинка та перезапуск. Він також створює файл PID, який може бути зручним для перевірки, чи демон вже працює тощо.

Ці зразки пояснюють, як створити демон. Чи є якісь додаткові речі, які потрібно врахувати? Чи є один зразок кращим за інший, і чому?


1
Я завжди вважав код демонізації непотрібним. Чому б просто не пустити оболонку?
emil.p.stanchev

17
Тому що це не робить setid або setpgrp.
bmargulies

4
Використовуйте supervisord.org . Таким чином, вам не потрібно розкручувати () або перенаправляти stdin / stderr. Просто напишіть звичайну програму.
guettli

Відповіді:


169

Поточне рішення

Довідкова реалізація PEP 3143 (Стандартна бібліотека демонових процесів) тепер доступна як пітон-демон .

Історична відповідь

Зразок коду Сандера Маречаля перевершує оригінал, який був спочатку опублікований у 2004 році. Я колись вносив демонізатор для Pyro, але, ймовірно, використовував би код Сандера, якби мені довелося це робити заново.


72
Редагувати: Оскільки я спочатку розмістив цю відповідь, посилання на реалізацію PEP 3143 тепер доступне: pypi.python.org/pypi/python-daemon
Джефф Бауер

@JeffBauer Оригінальна посилання померла, я пам'ятаю, що вона була корисною, ви б не випадково знали живу посилання для цього, чи не так?
CrazyCasta

1
@CrazyCasta: версія Сандер Маречаль все ще доступна на машині Wayback
Джефф Бауер,

1
@JeffBauer: Код Сандера все ще краще, ніж http://pypi.python.org/pypi/python-daemon. Більш надійний. Лише один приклад: спробуйте запустити два рази один і той же демон python-daemon: велика потворна помилка. З кодом Сандера: приємне повідомлення "Демон вже працює".
Бась

2
Оскільки документація модуля "python-daemon" все ще відсутня (див. Також багато інших запитань щодо SO) і є досить малозрозумілою (як правильно запустити / зупинити демон з командного рядка за допомогою цього модуля?), Я змінив зразок коду Сандера Маречаля, щоб додати quit()метод, який виконується до того, як демон буде зупинений. Ось.
Бась

163

Є багато примхливих речей, над якими слід подбати, коли станемо добре ведеться демон-процесом :

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

  • правильно поводитися всередині 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" написано правильно, тому що так написав W. Richard Stevens :-)
bignose

7
Гаол - річ англійська . Афіша з Австралії, тому це має сенс.
девін

1
Якісь плани щодо створення дружньої версії py3k?
Тім Тісдалл

97

Ось мій основний демон "Howdy World" Python, з якого я починаю, коли розробляю нову програму демон.

#!/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 пітон-демон | 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.
  • Відмовитись від привілеїв та жорстких привілеїв.
  • Закрийте всі відкриті дескриптори файлів.
  • Змініть робочий каталог, uid, gid та umask.
  • Встановіть відповідні обробники сигналів.
  • Відкрийте нові дескриптори файлів для stdin, stdout та stderr.
  • Керуйте вказаним файлом блокування PID.
  • Зареєструйте функції очищення для обробки на виході.

35

Альтернатива - створити нормальну, недемонізовану програму Python, а потім зовнішнє демонізувати її за допомогою нагляду . Це може врятувати багато головних болів, а також переносить * nix- та перекладати мову.


1
Я думаю, що це найкращий спосіб. Особливо, якщо ви хочете запустити кілька демонів в одній операційній системі. Не кодуйте, не використовуйте повторно.
guettli

Це спрощує безліч питань. Я написав справжні демони - вони непрості.
Кріс Джонсон

1
Найкраща відповідь ховається тут :)
kawing-chiu

1
Це золото. Провівши години, намагаючись пробігтися через пітон-демон, це рішення, що працює поза коробкою. Чудова документація та приклади змусили мого демона запуститися і пробігти за кілька хвилин.
Нікхіл Саху

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

Я віддаю перевагу цьому методу, тому що багато роботи робиться за вас, і тоді ваш демон-сценарій поводиться аналогічно решті вашої системи.

-Орбі


Це правильний і здоровий спосіб. 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.

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

Він може бути використаний як крапля заміни на пітон-демон.

Ось документація .


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.

2

Ще одне, над чим слід подумати, коли демонструєш пітон:

Якщо ви використовуєте журнал python і хочете продовжувати його використовувати після демонізування, обов'язково зателефонуйте close()до обробників (зокрема файлових).

Якщо цього не зробити, обробник все ще може подумати, що у ньому відкриті файли, а ваші повідомлення просто зникнуть - іншими словами, переконайтеся, що реєстратор знає, що його файли закриті!

Це передбачає, що під час демонстрації ви закриваєте ВСІ відкриті дескриптори файлів без розбору - замість цього ви можете спробувати закрити всі файли журналу, окрім файлів журналу (але, як правило, простіше закрити всі, а потім знову відкрити потрібні).


Ви вважаєте, що відкрити новий обробник журналу краще, ніж передавати обробник журналу до демона, використовуючи, наприклад, опцію files_preserve файлу DaemonContext?
HeyWatchЦе

Ви лише закриваєте реєстратор, ви не створюєте новий (він буде знову відкритий, коли потрібно). Але хоча це зробити насправді просто, можливо, краще використовувати DaemonContext, оскільки це, ймовірно, робить деякі інші розумні речі (якщо припустити, що збереження все ще дозволяє належну демонізацію).
Меттью Вілкокссон

2

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

Викликати його з python легко:

import ctypes

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

Залишилося лише створити (і заблокувати) PID-файл. Але що ти можеш впоратися сам ...


1

Я змінив кілька рядків у зразку коду Сандер Марешаль (згаданий @JeffBauer у прийнятій відповіді ), щоб додати quit()метод, який виконується до зупинки демона. Це іноді дуже корисно.

Ось.

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


-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, щоб зупинити його.


-2

Найпростіший спосіб створити демон з Python - це використовувати рамку Twisted, керовану подіями. Він обробляє всі речі, необхідні для демонізації для вас. Він використовує шаблон реактора для обробки одночасних запитів.


5
Це занадто великий молоток для використання. Більшість людей просто хочуть запустити короткий сценарій Python, який вони написали як демон. пітон-демон, як описано вище, є правильною відповіддю.
Tom Swirly

2
Хоча ця відповідь була досить зарозумілою, вона була корисною.
fiatjaf

-28

У 80% випадків, коли люди кажуть "демон", вони хочуть лише сервер. Оскільки в цьому питанні питання абсолютно незрозуміле, важко сказати, якою може бути область відповідей. Оскільки сервер адекватний, почніть з нього Якщо насправді потрібен "демон" (це рідко), читайте далі nohupяк спосіб демонізувати сервер.

Поки фактично не потрібен справжній демон, просто напишіть простий сервер.

Подивіться також на реалізацію посилань на WSGI .

Також дивіться простий сервер HTTP .

"Чи є якісь додаткові речі, які потрібно врахувати?" Так. Близько мільйона речей. Який протокол? Скільки запитів? Як довго обслуговувати кожен запит? Як часто вони прибуватимуть? Чи будете ви використовувати виділений процес? Нитки? Підпроцеси? Написати демон - велика робота.


12
Жодна з цих бібліотек навіть не робить жодної fork(), не кажучи вже про дві. Вони не мають нічого спільного з демонізацією.
Брендон Родос

8
В операційних системах Unix "демон", на зразок повітряних службовців, які греки назвали "демонами", - це "стоїть на боці". Замість того, щоб безпосередньо обслуговувати одного користувача через TTY цього користувача, демон не належить до TTY, але він може відповідати на запити багатьох користувачів в системі, або - як crondабо syslogd- надає послуги по утриманню всієї системи. Щоб створити демон-процес, потрібно принаймні виконати подвійний fork()із усіма закритими дескрипторами файлів, щоб не захищатись від сигналів з усіх керуючих терміналів, включаючи системну консоль. Дивіться відповідь bignose.
Брендон Родос

5
@S Lott - «сервер» описує, що робить процес (слухає вхідні запити замість ініціювання власних дій); "Демон" описує, як запускається процес (без вікна або керуючого терміналу). SimpleHTTPServerце справді сервер, але той, який не знає, як себе демонструвати (можна Ctrl-C, наприклад). nohup- це утиліта для демонізування наївного процесу - тому ваш неакупований сервер дійсно є і демон, і сервер, саме так, як ви заявляєте. Це питання щодо переповнення стека по суті запитувало: "Як я можу реалізувати nohupв Python?"
Брендон Родос

5
Так, але моє розуміння питання ОП полягає в тому, що він хоче зробити демонізацію в рамках своєї програми пітонів і не використовуючи щось інше.
Нуфал Ібрагім

4
@S Lott - Вас не потрібно вражати! Автор кожної іншої відповіді знав, що означає «демон», тому моя здатність тлумачити це питання навряд чи є унікальною. :) А звідки ви взяли думку про те, що я хочу, щоб автор заново винайшов колесо? Я думаю nohup, що це чудовий інструмент, і я зніму свій -1 голос, якщо ви просто перенесіть цю корисну ідею на свою фактичну відповідь. Насправді, якщо ви згадуєте supervisordі як це також вбереже автора від необхідності робити журнал, сценарій запуску-зупинки та перезапуск дроселювання, я навіть вам поставить +1. :)
Брендон Родос
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.