python实现Linux启动守护进程

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

原文地址:https://www.cnblogs.com/shhnwangjian/p/5918786.html