我编写了一个Python脚本,用于检查某个电子邮件地址并将新的电子邮件传递给外部程序。如何使此脚本在Linux中成为守护进程或服务并24/7运行?是否需要在程序中添加不断循环的代码,还是可以通过多次重新执行代码来实现?
这里有两个选项:
创建一个合适的cron工作,用于调用你的脚本。Cron是GNU/Linux守护程序的通用名称,它按照你设定的计划周期性地启动脚本。将脚本添加到crontab或将其符号链接放置到特殊目录中,守护程序会处理后台启动任务。你可以在维基百科上阅读更多信息。有各种不同的cron守护程序,但是你的GNU/Linux系统应该已经安装好了。
使用某种Python方法(例如库),使你的脚本能够自行守护。是的,它需要一个简单的事件循环(其中你的事件是由sleep函数提供的定时器触发)。
我不建议选择第二个选项,因为实际上你会重复cron的功能。Linux系统的范例是让多个简单的工具相互作用并解决你的问题。除非有其他原因需要创建守护进程(除了定期触发之外),否则请选择另一种方法。
此外,如果你使用daemonize并带有循环,当崩溃发生时,没有人会再检查邮件(正如Ivan Nevostruev在此答案的评论中所指出的)。而如果将脚本添加为cron工作,它只会再次触发。
.py
设置为每小时运行,那么它会创建许多永远不会终止的进程吗?如果是这样,我认为这就像守护进程。 - Veck Hsiao这是一个很好的类,取自这里:
#!/usr/bin/env python
import sys, os, time, atexit
from signal import SIGTERM
class Daemon:
"""
A generic daemon class.
Usage: subclass the Daemon class and override the run() method
"""
def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.pidfile = pidfile
def daemonize(self):
"""
do the UNIX double-fork magic, see Stevens' "Advanced
Programming in the UNIX Environment" for details (ISBN 0201563177)
http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
"""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError, e:
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
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, e:
sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = file(self.stdin, 'r')
so = file(self.stdout, 'a+')
se = file(self.stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# write pidfile
atexit.register(self.delpid)
pid = str(os.getpid())
file(self.pidfile,'w+').write("%s\n" % pid)
def delpid(self):
os.remove(self.pidfile)
def start(self):
"""
Start the daemon
"""
# Check for a pidfile to see if the daemon already runs
try:
pf = file(self.pidfile,'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if pid:
message = "pidfile %s already exist. Daemon already running?\n"
sys.stderr.write(message % self.pidfile)
sys.exit(1)
# Start the daemon
self.daemonize()
self.run()
def stop(self):
"""
Stop the daemon
"""
# Get the pid from the pidfile
try:
pf = file(self.pidfile,'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
if not pid:
message = "pidfile %s does not exist. Daemon not running?\n"
sys.stderr.write(message % self.pidfile)
return # not an error in a restart
# Try killing the daemon process
try:
while 1:
os.kill(pid, SIGTERM)
time.sleep(0.1)
except OSError, err:
err = str(err)
if err.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print str(err)
sys.exit(1)
def restart(self):
"""
Restart the daemon
"""
self.stop()
self.start()
def run(self):
"""
You should override this method when you subclass Daemon. It will be called after the process has been
daemonized by start() or restart().
"""
假设您真的想要让循环作为后台服务运行24/7
如果您不想使用任何库注入代码,可以创建一个服务模板,因为您正在使用Linux:
[Unit]
Description = <Your service description here>
After = network.target # Assuming you want to start after network interfaces are made available
[Service]
Type = simple
ExecStart = python <Path of the script you want to run>
User = # User to run the script as
Group = # Group to run the script as
Restart = on-failure # Restart when there are errors
SyslogIdentifier = <Name of logs for the service>
RestartSec = 5
TimeoutStartSec = infinity
[Install]
WantedBy = multi-user.target # Make it accessible to other users
将该文件放置于您的守护进程服务文件夹中(通常是/etc/systemd/system/
),在一个*.service
文件中,使用以下systemctl命令进行安装(可能需要sudo特权):
systemctl enable <service file name without .service extension>
systemctl daemon-reload
systemctl start <service file name without .service extension>
您可以使用以下命令检查服务是否正在运行:
systemctl | grep running
import os, sys
fpid = os.fork()
if fpid!=0:
# Running as daemon now. PID is fpid
sys.exit(0)
当然,您还需要实现一个无限循环,例如:
while 1:
do_your_check()
sleep(5)
希望这能让你开始。
nohup
/disown
命令会将进程从控制台中分离出来,使其不会死亡。或者您可以使用 init.d 启动它。 - pholat一个简单并且得到支持的版本是Daemonize
。
从Python包索引(PyPI)安装它:
$ pip install daemonize
然后像这样使用:
...
import os, sys
from daemonize import Daemonize
...
def main()
# your code here
if __name__ == '__main__':
myname=os.path.basename(sys.argv[0])
pidfile='/tmp/%s' % myname # any name
daemon = Daemonize(app=myname,pid=pidfile, action=main)
daemon.start()
您还可以使用shell脚本将Python脚本运行为服务。首先创建一个shell脚本来运行Python脚本,如下所示(scriptname是任意名称)
#!/bin/sh
script='/home/.. full path to script'
/usr/bin/python $script &
现在在 /etc/init.d/ 目录下创建一个名为 scriptname 的文件。
#! /bin/sh
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DAEMON=/home/.. path to shell script scriptname created to run python script
PIDFILE=/var/run/scriptname.pid
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting feedparser"
start_daemon -p $PIDFILE $DAEMON
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping feedparser"
killproc -p $PIDFILE $DAEMON
PID=`ps x |grep feed | head -1 | awk '{print $1}'`
kill -9 $PID
log_end_msg $?
;;
force-reload|restart)
$0 stop
$0 start
;;
status)
status_of_proc -p $PIDFILE $DAEMON atd && exit 0 || exit $?
;;
*)
echo "Usage: /etc/init.d/atd {start|stop|restart|force-reload|status}"
exit 1
;;
esac
exit 0
现在你可以使用命令 /etc/init.d/scriptname start 或 stop 来启动或停止你的 Python 脚本。
disown
来实现? - scribecron
显然是许多目的的很好的选择。 但它不会像你在 OP 中请求的那样创建服务或守护进程。 cron
只是定期运行作业(意味着作业启动和停止),且最多每分钟运行一次。 cron
存在问题--例如,如果您的脚本的先前实例仍在运行,下一次 cron
计划启动新实例时会发生什么?cron
不处理依赖项;它只会在计划指定的时间尝试启动作业。
如果您遇到真正需要守护进程(永远不停止运行的进程)的情况,请查看 supervisord
。 它提供了一种简单的方式来包装普通的非守护式脚本或程序,并使其像守护进程一样运行。 这比创建本地 Python 守护进程要好得多。
Ubuntu有一种非常简单的方式来管理服务。 对于Python来说,区别在于所有依赖项(软件包)都必须在同一个目录中,其中运行主文件。
我刚刚成功地创建了这样一个服务,向我的客户提供天气信息。 步骤:
Create your python application project as you normally do.
Install all dependencies locally like: sudo pip3 install package_name -t .
Create your command line variables and handle them in code (if you need any)
Create the service file. Something (minimalist) like:
[Unit]
Description=1Droid Weather meddleware provider
[Service]
Restart=always
User=root
WorkingDirectory=/home/ubuntu/weather
ExecStart=/usr/bin/python3 /home/ubuntu/weather/main.py httpport=9570 provider=OWMap
[Install]
WantedBy=multi-user.target
Save the file as myweather.service (for example)
Make sure that your app runs if started in the current directory
python3 main.py httpport=9570 provider=OWMap
The service file produced above and named myweather.service (important to have the extension .service) will be treated by the system as the name of your service. That is the name that you will use to interact with your service.
Copy the service file:
sudo cp myweather.service /lib/systemd/system/myweather.service
Refresh demon registry:
sudo systemctl daemon-reload
Stop the service (if it was running)
sudo service myweather stop
Start the service:
sudo service myweather start
Check the status (log file with where your print statements go):
tail -f /var/log/syslog
Or check the status with:
sudo service myweather status
Back to the start with another iteration if needed
该服务现在正在运行,即使您退出登录也不会受到影响。是的,如果主机关闭并重新启动,该服务将重新启动...
在Linux上使用$nohup
命令怎么样?
我在Bluehost服务器上使用它来运行我的命令。
如果我有错误,请指教。
systemd
的现代 Linux 上,您可以按照此处所述的方式创建一个以daemon
模式运行的 systemd 服务。另请参阅:https://www.freedesktop.org/software/systemd/man/systemd.service.html - ccpizza