python实现Linux启动守护进程
DaemonClass.py代码:
#/usr/bin/env python # -*- coding: utf-8 -*- import sys import os import time import atexit import subprocess from signal import SIGTERM BaseDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DIRS = {'ROOT': BaseDir, 'PID': '%s/var/guard.pid' % BaseDir, } class Daemon(object): """ daemon class. Usage: subclass the Daemon class and override the _run() method """ def __init__(self, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = DIRS['PID'] def _daemonize(self): """ @guard 守护进程主方法 """ # 脱离父进程 try: pid = os.fork() print("[_daemonize]pid:%s" % pid) if pid > 0: sys.exit(0) except OSError as e: sys.stderr.write("[_daemonize]fork #1 failed: %d (%s) " % (e.errno, e.strerror)) print("[_daemonize]fork #1 failed:"+str(e.strerror)) sys.exit(1) # 脱离终端 os.setsid() # 修改当前工作目录 os.chdir(DIRS['ROOT']) # 加载环境变量 guardpath = DIRS['ROOT'] sys.path.append(guardpath) # 重设文件创建权限 os.umask(0) # 第二次fork,禁止进程重新打开控制终端 try: pid = os.fork() if pid > 0: sys.exit(0) except OSError as e: sys.stderr.write("[_daemonize]fork #2 failed: %d (%s) " % (e.errno, e.strerror)) print("[_daemonize]fork #2 failed:"+str(e.strerror)) sys.exit(1) sys.stdout.flush() sys.stderr.flush() # 重定向文件描述符 with open(self.stdin, 'rb', 0) as f: os.dup2(f.fileno(), sys.stdin.fileno()) with open(self.stdout, 'ab', 0) as f: os.dup2(f.fileno(), sys.stdout.fileno()) with open(self.stderr, 'ab', 0) as f: os.dup2(f.fileno(), sys.stderr.fileno()) # 注册程序退出时的函数,即删掉pid文件 atexit.register(lambda: os.remove(self.pidfile)) pid = str(os.getpid()) file(self.pidfile, 'w+').write("%s " % pid) def start(self): """ Start the daemon """ # Check for a pidfile to see if the daemon already runs try: with open(self.pidfile, 'r') as pf: pid = int(pf.read().strip()) except IOError as e: pid = None print("daemon ioerror :"+str(e)) if pid: message = "Start error,pidfile %s already exist. Daemon already running? " print(message) 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: with open(self.pidfile, 'r') as pf: pid = int(pf.read().strip()) except IOError as err: pid = None print(err) if not pid: message = "pidfile %s does not exist. Daemon not running? " % self.pidfile print(message) sys.stderr.write(message) 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 as err: err = str(err) if err.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print('Stop error,'+str(err)) sys.exit(1) def status(self): """ Status the daemon """ # Get the pid from the pidfile try: with open(self.pidfile, 'r') as pf: pid = int(pf.read().strip()) except IOError as err: pid = None print(err) if not pid: message = "pidfile %s does not exist. Daemon not running? " % self.pidfile print(message) sys.stderr.write(message) else: p = subprocess.Popen('ps -ef|grep %s |grep -v grep' % pid, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) lines = p.stdout.readlines() print(lines) if len(lines) > 0: message = "pidfile %s exist. Daemon running! " % self.pidfile print(message) sys.stdout.write(message) else: message = "pidfile %s exist. But pid not exist, please administrator check process " % self.pidfile print(message) sys.stderr.write(message) def restart(self): """ Restart the daemon """ self.stop() time.sleep(0.1) 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(). """ raise NotImplementedError
MainClass.py 继承Daemon类
#/usr/bin/env python # -*- coding: utf-8 -*- import sys from DaemonClass import * import time import subprocess class ArgvHandler(Daemon): """ help_msg: 帮助方法 parse_argv: 参数检查 """ def __init__(self, argv_list): Daemon.__init__(self) self.argvs = argv_list print("程序输入参数:%s" % self.argvs) self.parse_argv() def parse_argv(self): """ :return: 获取执行程序后面的参数值,如果没有打印帮助内容 """ if len(self.argvs) > 1: if hasattr(self, self.argvs[1]): func = getattr(self, self.argvs[1]) func() else: self.help_msg() else: self.help_msg() def help_msg(self): print "Unknow Command!" print "Usage: %s start|stop|restart|status" % self.argvs[0] def _run(self): """ 监控入口 """ MonitorRun() def MonitorRun(): while 1: monitor_process('进程名') time.sleep(600) def monitor_process(processname): # 进程数量判断 try: p = subprocess.Popen('ps -ef|grep %s |grep -v grep' % processname, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) lines = p.stdout.readlines() if len(lines) == 1: print("进程名:%s, 数量:%s" % (processname, len(lines))) return else: print("进程名:%s, 数量:%s" % (processname, len(lines))) message = 'process[%s] is lost' % (processname) print(message) return message except Exception as err: message = "[monitor_process]%s" % err print(message) sys.stderr.write(message)
启动程序入口
#/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import platform if platform.system() == "Windows": BASE_DIR = '\'.join(os.path.abspath(os.path.dirname(__file__)).split('\')[:-1]) else: # for linux BASE_DIR = '/'.join(os.path.abspath(os.path.dirname(__file__)).split('/')[:-1]) sys.path.append(BASE_DIR) from lib import MainClass if __name__ == '__main__': MainClass.ArgvHandler(sys.argv)
参考文档:
http://blog.csdn.net/taiyang1987912/article/details/44850999
http://python3-cookbook-zh.readthedocs.io/zh_CN/latest/c12/p14_launching_daemon_process_on_unix.html