如何在Linux中使Python脚本像服务或守护程序一样运行
我写了一个Python脚本来检查某个电子邮件地址,并将新的电子邮件传递给外部程序。 我怎样才能让这个脚本全天候执行,例如在Linux中把它变成守护进程或服务。 我是否还需要一个循环,永远不会在程序中结束,还是可以通过多次执行代码来完成?
你有两个select。
-
做一个适当的cron工作 ,调用你的脚本。 Cron是GNU / Linux守护进程的通用名称,它根据您设置的计划定期启动脚本。 您可以将脚本添加到crontab中,或者将它的符号链接放到一个特殊的目录中,守护进程在后台处理启动它的工作。 你可以阅读更多在维基百科。 有各种不同的cron守护进程,但是你的GNU / Linux系统应该已经安装了。
-
使用某种Python方法 (例如,一个库)来使您的脚本能够自我守护。 是的,它将需要一个简单的事件循环(您的事件是定时器触发,可能由睡眠function提供)。
我不build议你select2.,因为你实际上重复cron的function。 Linux系统的范例是让多个简单的工具交互并解决你的问题。 除非有其他的原因,你应该做一个守护进程(除了定期触发),select另一种方法。
另外,如果你使用daemonize和一个循环并发生崩溃,没有人会在那之后检查邮件(正如Ivan Nevostruev在评论这个答案中所指出的那样)。 而如果脚本被添加为cron作业,则会再次触发。
这里有一个很好的课,
#!/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(). """
你应该使用python-daemon库,它处理所有事情。
从PyPI: Library实现一个行为良好的Unix守护进程。
您可以使用fork()将您的脚本从tty中分离出来,并继续运行,如下所示:
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)
希望这个得到你的开始。
您还可以使用shell脚本将python脚本作为服务运行。 首先创build一个shell脚本来运行这样的python脚本(脚本名任意名称)
#!/bin/sh script='/home/.. full path to script' /usr/bin/python $script &
现在在/etc/init.d/scriptname中创build一个文件
#! /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脚本。
如何在Linux上使用$nohup
命令?
我用它在我的Bluehost服务器上运行我的命令。
如果我错了,请咨询。
一个简单且受支持的版本是Deamonize从Python Package Index(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()
食谱278731:用Python的方式创build一个守护进程
首先,阅读邮件别名。 一个邮件别名可以在邮件系统内部完成,而不需要与守护进程或者服务或者其他任何东西混在一起。
您可以编写一个简单的脚本,每次将邮件发送到特定的邮箱时,该脚本将由sendmail执行。
请参阅http://www.feep.net/sendmail/tutorial/intro/aliases.html
如果你真的想写一个不必要的复杂的服务器,你可以这样做。
nohup python myscript.py &
这一切都需要。 你的脚本只是循环和睡觉。
import time def do_the_work(): # one round of polling -- checking email, whatever. while True: time.sleep( 600 ) # 10 min. try: do_the_work() except: pass
cron
显然是许多目的的一个很好的select。 但是,它不会像OP中所要求的那样创build服务或守护程序。 cron
只是定期运行作业(意味着作业开始和停止),而不会超过一次/分钟。 有cron
问题 – 例如,如果您的脚本的以前的实例仍在运行下一次cron
时间表周围,并启动一个新的实例,是吗? cron
不处理依赖关系; 它只是在计划表示时试图开始工作。
如果你发现你确实需要一个守护进程(一个永不停止运行的进程)的情况,请看看supervisord
。 它提供了一个简单的方法来包装一个正常的,非守护进程的脚本或程序,并使其像守护进程一样运行。 这比创build本地Python守护进程要好得多。
如果您使用的是terminal(ssh或者其他),并且您希望在从terminal注销后保留长时间的脚本,则可以尝试以下操作:
screen
apt-get install screen
在里面创build一个虚拟terminal(即abc): screen -dmS abc
现在我们连接到abc: screen -r abc
所以,现在我们可以运行python脚本: python Keep_sending_mail.py
从现在开始,你可以直接closures你的terminal,但是python脚本将继续运行而不是被closures
由于这个
Keep_sending_mail.py
的PID属于虚拟屏幕而不是terminal(ssh)
如果你想回去检查你的脚本运行状态,可以再次使用screen -r abc
使用你的系统提供的任何服务pipe理器 – 例如在Ubuntu下使用新贵 。 这将处理所有的细节,例如启动时启动,崩溃时重新启动等。
有类似的问题,下面的链接为我工作: http : //werxltd.com/wp/2012/01/05/simple-init-d-script-template/#footnote_0_1077
它没有使用任何特定的分配,如果使用chkconfig ,可以在系统启动时启动。
我会推荐这个解决scheme。 您需要inheritance并重写方法run
。
import sys import os from signal import SIGTERM from abc import ABCMeta, abstractmethod class Daemon(object): __metaclass__ = ABCMeta def __init__(self, pidfile): self._pidfile = pidfile @abstractmethod def run(self): pass def _daemonize(self): # decouple threads pid = os.fork() # stop first thread if pid > 0: sys.exit(0) # write pid into a pidfile with open(self._pidfile, 'w') as f: print >> f, os.getpid() def start(self): # if daemon is started throw an error if os.path.exists(self._pidfile): raise Exception("Daemon is already started") # create and switch to daemon thread self._daemonize() # run the body of the daemon self.run() def stop(self): # check the pidfile existing if os.path.exists(self._pidfile): # read pid from the file with open(self._pidfile, 'r') as f: pid = int(f.read().strip()) # remove the pidfile os.remove(self._pidfile) # kill daemon os.kill(pid, SIGTERM) else: raise Exception("Daemon is not started") def restart(self): self.stop() self.start()
创造一些像服务一样运行的东西,你可以使用这个东西:
你必须做的第一件事是安装水泥框架:水泥框架工作是一个CLI框架的工作,你可以部署你的应用程序。
应用程序的命令行界面:
interface.py
from cement.core.foundation import CementApp from cement.core.controller import CementBaseController, expose from YourApp import yourApp class Meta: label = 'base' description = "your application description" arguments = [ (['-r' , '--run'], dict(action='store_true', help='Run your application')), (['-v', '--version'], dict(action='version', version="Your app version")), ] (['-s', '--stop'], dict(action='store_true', help="Stop your application")), ] @expose(hide=True) def default(self): if self.app.pargs.run: #Start to running the your app from there ! YourApp.yourApp() if self.app.pargs.stop: #Stop your application YourApp.yourApp.stop() class App(CementApp): class Meta: label = 'Uptime' base_controller = 'base' handlers = [MyBaseController] with App() as app: app.run()
YourApp.py类:
import threading class yourApp: def __init__: self.loger = log_exception.exception_loger() thread = threading.Thread(target=self.start, args=()) thread.daemon = True thread.start() def start(self): #Do every thing you want pass def stop(self): #Do some things to stop your application
请记住,您的应用程序必须在线程上运行才能成为守护进程
要运行应用程序,只需在命令行中执行此操作
python interface.py –help