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)\n" % (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)\n" % (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\n" % 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?\n"
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?\n" % 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?\n" % 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!\n" % self.pidfile
print(message)
sys.stdout.write(message)
else:
message = "pidfile %s exist. But pid not exist, please administrator check process\n" % 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