Python编写守护进程程序思路

# !/usr/bin/env python
# coding: utf-8
 
# python模拟linux的守护进程
 
import sys, os, time, atexit, string
from signal import SIGTERM
 
__metaclass__ = type
 
 
class Daemon:
    def __init__(self, pidfile="/tmp/Daemon.pid", stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
        # 需要获取调试信息,改为stdin='/dev/stdin', stdout='/dev/stdout', stderr='/dev/stderr',以root身份运行。
        self.stdin = stdin
        self.stdout = stdout
        self.stderr = stderr
        self.pidfile = pidfile
        self.applicationName = "Application"
        self._homeDir = "/"
        # 调试模式是否开启
        self._verbose = False
        # 用户掩码,默认为0
        self._umask = 0
 
    # 获取守护进程掩码
    @property
    def umask(self):
        return self._umask
 
    # 设置守护进程掩码
    @umask.setter
    def umask(self, umask):
        self._umask = umask
 
    # 获取当前是否是调试模式
    @property
    def VerboseMode(self):
        return self._verbose
 
    # 调试模式开关,默认不是调试模式
    @VerboseMode.setter
    def VerboseMode(self, verboseMode):
        self._verbose = verboseMode
 
    # 调试模式和非调试模式设置
    def _verbosSwitch(self):
        # 调试模式是输出日志到指定文件,这些文件在对象初始化时指定
        if self._verbose:
            pass
            # self.stdin = '/dev/stdin'
            # self.stdout = '/dev/stdout'
            # self.stderr = '/dev/stderr'
        else:
            self.stdin = '/dev/null'
            self.stdout = '/dev/null'
            self.stderr = '/dev/null'
 
    def setApplicationName(self, appName):
        self.applicationName = appName
 
    # 获取和设置进程住目录
    @property
    def HomeDir(self):
        return self._homeDir
 
    @HomeDir.setter
    def HomeDir(self, homeDir):
        self._homeDir = homeDir
 
    # 这个方法的主要目的就是脱离主体,为进程创造环境
    def _daemonize(self):
        # 第一步
        try:
            # 第一次fork,生成子进程,脱离父进程,它会返回两次,PID如果等于0说明是在子进程里面,如果大于0说明当前是在父进程里
            pid = os.fork()
            # 如果PID大于0,说明当前在父进程里,然后sys.exit(0),则是退出父进程,此时子进程还在运行。
            if pid > 0:
                # 退出父进程,此时linux系统的init将会接管子进程
                sys.exit(0)
        except OSError, e:
            sys.stderr.write('fork #1 failed: %d (%s)\n' % (e.errno, e.strerror))
            sys.exit(1)
 
        # 第二、三、四步
        os.chdir("/")  # 修改进程工作目录
        os.setsid()  # 设置新的会话,子进程会成为新会话的首进程,同时也产生一个新的进程组,该进程组ID与会话ID相同
        os.umask(self._umask)  # 重新设置文件创建权限,也就是工作目录的umask
 
        # 第五步
        try:
            # 第二次fork,禁止进程打开终端,相当于是子进程有派生一个子进程
            pid = os.fork()
            if pid > 0:
                # 子进程退出,孙子进程运行,此时孙子进程由init进程接管,在CentOS 7中是Systemed。
                sys.exit(0)
        except OSError, e:
            sys.stderr.write('fork #2 failed: %d (%s)\n' % (e.errno, e.strerror))
            sys.exit(1)
 
        # 第六步
        # 把之前的刷到硬盘上
        sys.stdout.flush()
        sys.stderr.flush()
        # 重定向标准文件描述符
        si = file(self.stdin, 'r')
        so = file(self.stdout, 'a+')
        se = file(self.stderr, 'a+', 0)
        # os.dup2可以原子化的打开和复制描述符,功能是复制文件描述符fd到fd2, 如果有需要首先关闭fd2. 在unix,Windows中有效。
        # File的 fileno() 方法返回一个整型的文件描述符(file descriptor FD 整型)
        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 = str(os.getpid())
        file(self.pidfile, 'w+').write('%s\n' % pid)
 
    # 程序退出后移除PID文件
    def delpid(self):
        os.remove(self.pidfile)
 
    def start(self, *args, **kwargs):
        # 检查pid文件是否存在以探测是否存在进程
        try:
            pid = self._getPid()
        except IOError:
            pid = None
 
        # 如果PID存在,则说明进程没有关闭。
        if pid:
            message = 'pidfile %s already exist. Process already running!\n'
            sys.stderr.write(message % self.pidfile)
            # 程序退出
            sys.exit(1)
 
        # 构造进程环境
        self._daemonize()
        # 执行具体任务
        self._run(*args, **kwargs)
 
    def stop(self):
        # 从pid文件中获取pid
        try:
            pid = self._getPid()
        except IOError:
            pid = None
 
        # 如果程序没有启动就直接返回不在执行
        if not pid:
            message = 'pidfile %s does not exist. Process not running!\n'
            sys.stderr.write(message % self.pidfile)
            return
 
        # 杀进程
        try:
            while 1:
                # 发送信号,杀死进程
                os.kill(pid, SIGTERM)
                time.sleep(0.1)
                message = 'Process is stopped.\n'
                sys.stderr.write(message)
        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)
 
    # 获取PID
    def _getPid(self):
        try:
            # 读取保存PID的文件
            pf = file(self.pidfile, 'r')
            # 转换成整数
            pid = int(pf.read().strip())
            # 关闭文件
            pf.close()
        except IOError:
            pid = None
        except SystemExit:
            pid = None
        return pid
 
    # 重启的功能就是杀死之前的进程,然后再运行一个
    def restart(self, *args, **kwargs):
        self.stop()
        self.start(*args, **kwargs)
 
    # 获取守护程序运行状态
    def status(self):
        try:
            pid = self._getPid()
        except IOError:
            pid = None
 
        if not pid:
            message = "No such a process running.\n"
            sys.stderr.write(message)
        else:
            message = "The process is running, PID is %s .\n"
            sys.stderr.write(message % str(pid))
 
    def _run(self, *args, **kwargs):
        """
        这里是孙子进程需要做的事情,你可以继承这个类,然后重写这里的代码,上面其他的都可以不做修改
        """
        while True:
            """
            print 等于调用 sys.stdout.write(), sys.stdout.flush()是立即刷新输出。正常情况下如果是输出到控制台那么会立即输出
            但是重定向到一个文件就不会了,因为等于写文件,所以需要进行刷新进行立即输出。 下面使用print 还是 write都是一样的。
            """
            # print '%s:hello world\n' % (time.ctime(),)
            sys.stdout.write('%s:hello world\n' % (time.ctime(),))
            sys.stdout.flush()
            time.sleep(2)
 
 
if __name__ == '__main__':
    daemon = Daemon('/tmp/watch_process.pid', stdout='/tmp/watch_stdout.log')
    if len(sys.argv) == 2:
        if 'start' == sys.argv[1]:
            daemon.setApplicationName(sys.argv[0])
            daemon.start()
        elif 'stop' == sys.argv[1]:
            daemon.stop()
        elif 'restart' == sys.argv[1]:
            daemon.restart()
        elif 'status' == sys.argv[1]:
            daemon.status()
        else:
            print 'unknown command'
            sys.exit(2)
        sys.exit(0)
    else:
        print 'usage: %s start|stop|restart|status' % sys.argv[0]
        sys.exit(2)

本文转至https://www.cnblogs.com/rexcheny/p/9382342.html

为了留个备份...以后好找,索性就把代码copy过来了。。 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值