如何在Python中创建守护进程?

Dav*_*idM 237 python daemon

在Google上搜索会显示x2代码段.第一个结果是这个代码配方有很多文档和解释,以及下面的一些有用的讨论.

但是,另一个代码示例虽然不包含如此多的文档,但包含用于传递命令(如启动,停止和重新启动)的示例代码.它还会创建一个PID文件,可以方便地检查守护程序是否已在运行等.

这些示例都解释了如何创建守护进程.还有其他事情需要考虑吗?一个样本比另一个好,为什么?

big*_*ose 162

在成为一个表现良好的守护进程时,有许多需要注意的事情:

  • 防止核心转储(许多守护程序以root身份运行,核心转储可能包含敏感信息)

  • chroot监狱里表现正确

  • 为用例适当地设置UID,GID,工作目录,umask和其他进程参数

  • 放弃提升suid,sgid特权

  • 关闭所有打开的文件描述符,具有取决于用例的排除

  • 正确的行为,如果启动一个已经脱离上下文中,如init,inetd

  • 为敏感的守护程序行为设置信号处理程序,但也使用由用例确定的特定处理程序

  • 重定向标准流stdin,stdout,stderr因为守护进程不再具有控制终端

  • 处理一个PID文件作为合作咨询锁,这本身就是一整套蠕虫,有很多矛盾但有效的行为方式

  • 在进程终止时允许适当的清理

  • 实际上成为一个守护进程而不会导致僵尸

其中一些是标准的,如规范的Unix文献(UNIX环境中的高级编程,已故的W. Richard Stevens,Addison-Wesley,1992)中所述.其他的,例如流重定向和PID文件处理,是大多数守护程序用户期望的常规行为,但是标准化程度较低.

所有这些都包含在PEP 3143 "标准守护进程库"规范中.该蟒蛇守护参考实现工作在Python 2.7版或更高版本,和Python 3.2或更高版本.

  • "监狱"拼写正确,因为这就是理查德史蒂文斯拼写它的方式:-) (25认同)
  • 监狱是[英语事物](http://en.wiktionary.org/wiki/gaol).这张海报来自澳大利亚,所以它很有意义. (7认同)
  • 这是过时的监狱拼写.(至少在这里过时,可能不在其他地方) (2认同)

Jef*_*uer 161

当前解决方案

PEP 3143(标准守护程序进程库)的参考实现现在可用作python-daemon.

历史答案

Sander Marechal的代码示例优于原始版本,最初发布于2004年.我曾经为Pyro贡献了一个守护程序,但如果我不得不这样做,可能会使用Sander的代码.

  • 编辑:由于我最初发布此回复,现在可以使用PEP 3143的参考实现:http://pypi.python.org/pypi/python-daemon/ (70认同)
  • 由于“python-daemon”模块文档仍然缺失(另请参阅许多其他 SO 问题)并且相当模糊(如何使用此模块从命令行正确启动/停止守护程序?),我修改了 Sander Marechal 的代码示例以添加在守护进程停止之前执行的 `quit()` 方法。[在这里。](https://gist.github.com/josephernest/77fdb0012b72ebdf4c9d19d6256a1119) (2认同)

Dus*_*and 94

这是我开始使用的基本"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()
Run Code Online (Sandbox Code Playgroud)

请注意,您需要该python-daemon库.您可以通过以下方式安装:

pip install python-daemon
Run Code Online (Sandbox Code Playgroud)

然后启动它./howdy.py start,然后停止它./howdy.py stop.

  • 我按照你的描述安装了python-daemon,但是当我尝试运行我的应用程序时(与最后3行相同),我得到ImportError:无法导入名称转换器 (6认同)
  • 您导入的"守护进程"模块不是Python的标准部分(尚未).它需要与`pip install python-daemon`或同等版本一起安装. (5认同)
  • 这个建议似乎已经过时了 - 截至2013年9月,无论如何,http://www.python.org/dev/peps/pep-3143/都没有提到可以导入的"跑步者".这当然可以解释@Nostradamnit的观察. (4认同)
  • 2013年9月,在Ubuntu 13.04上安装了python2.7和python-daemon,这对我来说仍然可以正常工作.但是,使用python3,我看到错误,"来自守护程序导入运行器ImportError:没有名为'daemon'的模块" (2认同)

Vil*_*iam 42

注意python-daemon包解决了守护进程背后的许多问题.

它可以实现的其他功能(来自Debian软件包描述):

  • 将进程分离到其自己的进程组中.
  • 设置适合在chroot内运行的进程环境.
  • 放弃suid和sgid权限.
  • 关闭所有打开的文件描述符.
  • 更改工作目录,uid,gid和umask.
  • 设置适当的信号处理程序.
  • 打开stdin,stdout和stderr的新文件描述符.
  • 管理指定的PID锁定文件.
  • 注册清理函数以进行退出处理.


Chr*_*son 31

另一种方法 - 创建一个普通的非守护程序的Python程序,然后使用supervisord从外部守护它.这可以节省很多麻烦,并且*nix-和语言可移植.


Luk*_*pin 12

可能不是问题的直接答案,但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
Run Code Online (Sandbox Code Playgroud)

我更喜欢这种方法,因为很多工作都是为您完成的,然后您的守护程序脚本的行为与系统的其他部分类似.

-Orby

  • 如果你的环境中有systemd,这是个不错的选择. (3认同)
  • 这才是正确、理智的做法。1) 需要保存到 /etc/systemd/system/control.service 2) 托管 sudo `systemctl start control.service` (3认同)

Iva*_*kov 7

此函数将应用程序转换为守护程序:

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())
Run Code Online (Sandbox Code Playgroud)


小智 6

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的替代品.

这是文档.


Som*_*mum 6

恐怕@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.
Run Code Online (Sandbox Code Playgroud)

跑步很容易

> python myDaemon.py
Run Code Online (Sandbox Code Playgroud)

只是为了完整起见,这里是 samplemodule 目录内容

>ls samplemodule
__init__.py __init__.pyc moduleclass.py
Run Code Online (Sandbox Code Playgroud)

moduleclass.py 的内容可以是

class moduleclass():
    ...

def do_running():
    m = moduleclass()
    # do whatever daemon is required to do.
Run Code Online (Sandbox Code Playgroud)