Python创建守护进程

daemon.py:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import signal

__author__ = 'lnp'

import atexit
import sys
import socket
import os

#守护进程包装类
class Daemon:

    def __init__(self,pid_file,stdout=os.devnull,stderr=os.devnull):
        self.pid_file = pid_file
        self.stderr = stderr
        self.stdout =stdout

    '''
        编写守护进程的一般步骤步骤:
        (1)创建自己成并被init进程接管:在父进程中执行fork并exit退出;
        (2)创建新进程组和新会话:在子进程中调用setsid函数创建新的会话;
        (3)修改子进程的工作目录:在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录;
        (4)修改子进程umask:在子进程中调用umask函数,设置进程的umask为0;
        (5)在子进程中关闭任何不需要的文件描述符
        在子进程中再次fork一个进程,这个进程称为孙子进程,之后子进程退出
        重定向孙子进程的标准输入流、标准输出流、标准错误流到/dev/null
        那么最终的孙子进程就称为守护进程。
        '''
    def __daemonize(self):
        # 从父进程fork一个子进程出来
        pid = os.fork()
        # 子进程的pid一定为0,父进程大于0
        if pid:
            #退出父进程
            sys.exit(0)
        # 子进程默认继承父进程的工作目录,最好是变更到根目录,否则回影响文件系统的卸载
        os.chdir('/')
        # 子进程默认继承父进程的umask(文件权限掩码),重设为0(完全控制),以免影响程序读写文件
        os.umask(0)
        # 让子进程成为新的会话组长和进程组长
        os.setsid()
        # 第2次fork,也就是子进程的子进程,我们把它叫为孙子进程
        _pid = os.fork()
        if _pid:
            # 退出子进程
            sys.exit(0)
        # 此时,孙子进程已经是守护进程了,接下来重定向标准输入、输出、错误的描述符(是重定向而不是关闭, 这样可以避免程序在 print 的时候出错)
        # 刷新缓冲区先,小心使得万年船
        sys.stdout.flush()
        sys.stderr.flush()
        si = open(os.devnull, 'r')
        so = open(self.stdout, 'a+')
        if self.stderr:
            se = open(self.stderr,'a+')
        else:
            se = so
        # dup2函数原子化地关闭和复制文件描述符,重定向到/dev/nul,即丢弃所有输入输出
        os.dup2(si.fileno(), sys.stdin.fileno())
        os.dup2(so.fileno(), sys.stdout.fileno())
        os.dup2(se.fileno(), sys.stderr.fileno())

        # 注册退出函数,进程退出时移除pid文件
        atexit.register(self.delpid)

        # 写入pid文件
        if self.pid_file:
            with open(self.pid_file,'w+') as f:
                f.write(str(os.getpid()))

    # 程序退出后移除PID文件
    def delpid(self):
        os.remove(self.pid_file)

    #获取pid
    def get_pid(self):
        if os.path.exists(self.pid_file):
            with open(self.pid_file,'r') as f:
                pid = int(f.read().strip())
                return pid
        else:
            return None

    # 核心业务逻辑
    def __run(self):
        #执行你的业务逻辑。。。。

    # 启动程序
    def start(self):
        print("Starting...")
        self.__daemonize()
        self.__run()

    #停止程序
    def stop(self):
        print("Stopping...")
        pid = self.get_pid()
        try:
            # 终止守护进程
            while True:
                if pid:
                    os.kill(pid, signal.SIGKILL)
        except OSError as err:
            if str(err).find('No such process') >0:
                if os.path.exists(self.pid_file):
                    os.remove(self.pid_file)
            else:
                print(str(err))
                sys.exit(1) #异常退出

if __name__ == '__main__':
    pid_file = "/tmp/daemon.pid"    # 守护进程pid文件的绝对路径
    out_fn = '/tmp/daemon.out.log'  # 守护进程标准输出日志,
    err_fn = '/tmp/daemon.err.log'  # 守护进程启动过程中的错误日志,内部出错能从这里看到
    daemon = Daemon(pid_file,out_fn,err_fn)
    if sys.argv[1] == 'start':
        daemon.start()
    elif sys.argv[1] == 'stop':
        daemon.stop()
    else:
        print('invalid argument!')

使用 python deamon.py start 命令就可以守护进程的方式运行程序啦!

当然,这里只支持了start和stop命令,需要的也可扩充status等命令

猜你喜欢

转载自blog.csdn.net/ln1593570p/article/details/107882721