在Google上搜索会发现x2代码段。第一个结果是该代码配方的内容,其中包含大量文档和说明,并在下面进行了一些有用的讨论。
但是,另一个代码示例虽然没有包含太多文档,但包含用于传递命令(例如启动,停止和重新启动)的示例代码。它还会创建一个PID文件,可以方便地检查守护程序是否已在运行等。
这些示例都说明了如何创建守护程序。还有其他需要考虑的事情吗?一个样本比另一个样本好吗,为什么?
在Google上搜索会发现x2代码段。第一个结果是该代码配方的内容,其中包含大量文档和说明,并在下面进行了一些有用的讨论。
但是,另一个代码示例虽然没有包含太多文档,但包含用于传递命令(例如启动,停止和重新启动)的示例代码。它还会创建一个PID文件,可以方便地检查守护程序是否已在运行等。
这些示例都说明了如何创建守护程序。还有其他需要考虑的事情吗?一个样本比另一个样本好吗,为什么?
Answers:
当前解决方案
PEP 3143(标准守护程序进程库)的参考实现现已作为python-daemon提供。
历史答案
Sander Marechal的代码示例优于最初于2004年发布的原始代码示例。我曾经为Pyro提供了一个守护程序,但如果不得不这样做,可能会使用Sander的代码。
http://pypi.python.org/pypi/python-daemon
。更可靠。只是一个示例:尝试 使用:big ugly error 启动两次相同的守护程序python-daemon
。使用Sander的代码:一个不错的通知“守护程序已经运行”。
成为行为良好的守护进程时,有很多事情要注意:
防止核心转储(许多守护程序以root身份运行,并且核心转储可以包含敏感信息)
在chroot
监狱中表现正确
根据使用情况适当设置UID,GID,工作目录,umask和其他过程参数
放弃升高suid
,sgid
特权
关闭所有打开的文件描述符,并根据用例进行排除
正确的行为,如果启动一个已经脱离上下文中,如init
,inetd
等
为明智的守护程序行为设置信号处理程序,还可以根据用例确定特定的处理程序
重定向标准流stdin
,stdout
,stderr
因为守护进程不再具有控制终端
在进程终止时允许适当的清理
实际上成为守护进程而不会导致僵尸
其中一些是标准的,如规范的UNIX文献所述(UNIX环境中的Advanced Programming,由已故的W. Richard Stevens,Addison-Wesley,1992年出版)。其他(例如流重定向和PID文件处理)是大多数守护程序用户期望的常规行为,但标准化程度较低。
PEP 3143 “标准守护程序进程库”规范涵盖了所有这些内容。该蟒蛇守护参考实现工作在Python 2.7版或更高版本,和Python 3.2或更高版本。
在开发新的守护程序应用程序时,这是我的基本“ 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
。
daemon
您导入模块而不是Python(还)标准的一部分。它需要与pip install python-daemon
或等效安装。
请注意python-daemon软件包,该软件包可立即解决守护程序背后的许多问题。
它支持的其他功能(来自Debian软件包描述):
一种替代方法-创建一个普通的,非守护进程的Python程序,然后使用supervisor在外部对其进行守护进程。这可以节省很多麻烦,并且* nix和语言可移植。
可能不是该问题的直接答案,但是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
我喜欢这种方法,因为为您完成了很多工作,然后守护程序脚本的行为与系统其余部分的行为类似。
-奥比
systemctl start control.service
由于python-daemon尚不支持python 3.x,并且从邮件列表中可以读取的内容来看,它可能永远不会支持,因此我编写了PEP 3143的新实现:pep3143daemon
pep3143daemon至少应支持python 2.6、2.7和3.x
它还包含一个PidFile类。
该库仅取决于标准库和六个模块。
它可以用作python-daemon的替代品。
这是文档。
此函数会将应用程序转换为守护程序:
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())
恐怕@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.
在python守护进程中要考虑的一件事:
如果您正在使用python 日志记录,并且希望在守护进程后继续使用它,请确保调用close()
处理程序(尤其是文件处理程序)。
如果您不这样做,那么处理程序仍然可以认为它已打开文件,并且您的消息将完全消失-换句话说,请确保记录器知道其文件已关闭!
这假定您在守护程序时无差别地关闭所有打开的文件描述符-相反,您可以尝试关闭除日志文件以外的所有文件(但是通常先关闭所有文件然后再重新打开所需的文件更简单)。
经过几年的尝试(我尝试了这里给出的所有答案,但最后都没有什么缺点),现在我意识到,有比直接从Python启动,停止,重新启动守护程序更好的方法:改用OS工具。
例如,对于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停止它。
80%的情况下,当人们说“守护程序”时,他们只想要一台服务器。由于这一点上的问题尚不清楚,因此很难说出答案的可能范围。由于服务器已足够,因此从那里开始。如果确实需要一个实际的“守护程序”(这种情况很少见),请继续阅读nohup
作为守护服务器的一种方式。
在需要实际的守护程序之前,只需编写一个简单的服务器即可。
另请参阅WSGI参考实现。
另请参阅简单HTTP服务器。
“还有其他需要考虑的事情吗?”是的。大约一百万的东西。什么协议?有多少个请求?每个请求服务多长时间?他们多久到达一次?您会使用专门的流程吗?线程?子流程?编写守护程序是一项艰巨的工作。
fork()
,更不用说两个了。它们与守护进程无关。
crond
或syslogd
-为整个系统提供客房服务。要创建一个守护进程,必须至少在fork()
关闭所有文件描述符的情况下执行double- ,以便一个进程不受来自所有控制终端(包括系统控制台)的信号的影响。参见bignose的答案。
SimpleHTTPServer
确实是一台服务器,但是它本身并不知道如何守护它(例如,您可以Ctrl-C来控制它)。nohup
是一个用于守护天真进程的实用程序-因此,正如您所声称的那样,您已停顿的服务器确实既是守护程序又是服务器。这个堆栈溢出问题本质上是在问:“如何nohup
在Python中实现?”
nohup
是个很好的工具,如果您只是将有用的想法移到您的实际答案中,我将删除-1票。实际上,如果您提到supervisord
了它,以及它将如何使作者免于必须进行日志记录,起止脚本以及重新启动调节的麻烦,那么我什至会为您+1。:)