• python 守护进程


    在linux环境上,使用守护进程保护python程序在后台运行,不受会话控制终端影响。

    0x01 守护进程和后台运行的区别:

    1、让程序在后台运行,只用在程序启动的时候在结尾加&,这样在控制终端进行输入不会影响程序的运行。

    如python main.py&  程序启动后,可以在该控制继续进行输入,不影响main.py的运行。但是如果关闭会话连接,main.py进程就关闭了。

    2、后台运行的程序有控制终端,守护进程没有。即如果main.py运行的会话连接断开,不影响main.py进程的运行。

         父进程退出后,子进程被pid为1的进程(init进程)接管,时候子进程不受终端退出的影响。

    0x02 守护进程编程原理:

    1、创建子进程,父进程退出

    使用os.fork创建进程,并将父进程退出。  

    目的:让子进程脱离父进程控制,被init进程接管,即变成pid为1的进程的子进程。

    2、在子进程中创建新的会话

    使用setid创建新会话,并担任该会话组的组长。

    目的:

    • 让进程摆脱原会话的控制
    • 让进程摆脱原进程组的控制
    • 让进程摆脱原控制终端的控制

    由于创建守护进程的第一步调用了fork函数来创建子进程,再将父进程退出。由于在调用了fork函数时,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。

    3、重设文件权限掩码

    使用umask(0)重新设置文件权限,是为了去掉父进程遗留的文件权限设置。

    4、禁止进程重新打开控制终端

    在基于SystemV的系统中,有建议再一次调用fork 并使父进程退出。而新产生的进程将会成为真正的守护进程。这一步骤将保证守护进程不是一个sessionleader,进而阻止它获取一个控制终端。

    第二次fork不是必须的,打开一个控制终端的条件是该进程必须是session leader。第一次fork,setsid之后,子进程成为session leader,进程可以打开终端;第二次fork产生的进程,不再是session leader,进程则无法打开终端。

    5、改变当前工作目录

    防止占用别的路径的working dir的fd,导致一些block不能unmount。但注意这里修改了以后,守护进程下面的子进程log打印就都在被修改的路径下了。

    0x03 DEMO

    #!/usr/bin/env python
    #coding: utf-8
    #pythonlinux的守护进程
    
    import sys
    import os
    import time
    import string
    import ctypes
    import datetime
    from logger import *
    
    logyyx = Logger('tsl.log', logging.ERROR, logging.DEBUG)
    
    class Daemon:
        def __init__(self, findCmd, runCmd, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
            self.findCmd = findCmd
            self.runCmd = runCmd
            self.stdin = stdin
            self.stdout = stdout
            self.stderr = stderr
            #self.logger = logging.getLogger()
        '''
        def LoggerInit(self):
            logfile = '/home/***/log/tsl.log'
            hdlr=logging.FileHandler(logfile)
            formatter = logging.Formatter('
    %(asctime)s   %(filename)s[line:%(lineno)d]   %(levelname)s
    %(message)s')
            hdlr.setFormatter(formatter)
            self.logger.addHandler(hdlr)
            self.logger.setLevel(logging.NOTSET)
            return
        '''
        def daemonize(self):
            try:
                #第一次fork,生成子进程,脱离父进程
                if os.fork() > 0:
                    raise SystemExit(0)      #退出主进程
            except OSError as e:
                logyyx.error("fork #1 failed:
    ")
                #sys.exit(1)
                raise RuntimeError('fork #1 faild: {0} ({1})
    '.format(e.errno, e.strerror))
    
            os.setsid()        #设置新的会话连接
            os.umask(0)        #重新设置文件创建权限
            try:
                #第二次fork,禁止进程打开终端
                if os.fork() > 0:
                    raise SystemExit(0)
            except OSError as e:
                logyyx.error("fork #2 failed:
    ")
                #sys.exit(1)
                raise RuntimeError('fork #2 faild: {0} ({1})
    '.format(e.errno, e.strerror))
            os.chdir("/")  # 修改工作目录
            # Flush I/O buffers
            sys.stdout.flush()
            sys.stderr.flush()
    
            # Replace file descriptors for stdin, stdout, and stderr
            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())
    
            return
    
        def start(self):
            #检查pid文件是否存在以探测是否存在进程
            esb = os.popen(self.findCmd).read().strip()
            if not (esb == '0'):
                print"the deamon is already running!!!"
                return
            else:
                #启动监控
                self.daemonize()
                self.run()
    
        def run(self):
            now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            while True:
                try:
                    esb = os.popen(self.findCmd).read().strip()
                    if (esb == '0'):
                        logyyx.info("deamon on  %s" %now)
                        os.system(self.runCmd)
                except:
                    pass
                time.sleep(10)
    
        def KillPid(self,name):
            ps_str = 'ps aux |grep '+name+' | grep -v grep'
            x= os.popen(ps_str).read()
            if x:
                proc = x.split('
    ')
                for line in proc:
                    print line
                    try:
                        proc_id = line.split()[1]
                        os.system('kill -9 %s' % proc_id)
                    except:
                        pass
            else:
                return
    
        def checkpid(self, name):
            findCmd='ps -fe |grep '+name+' | grep -v grep | wc -l'
            esb = os.popen(findCmd).read().strip()
            if not (esb == '0'):
                #杀进程
                try:
                    self.KillPid(name)
                except:
                    print"kill %s failed!!!" % name
                    logyyx.error("the deamon  %s  kill failed" % name)
                    return
            return
        def stop(self):
            self.checkpid('main.py')
            self.checkpid('deamon.py')
            return
    
        def restart(self):
            self.stop()
            self.start()
    
    if __name__ == "__main__":
        findCmd = 'ps -fe |grep main.py | grep -v grep | wc -l'
        runCmd = 'python /home/***/main.py'
        LOG = './tsl.log'
        daemon = Daemon(findCmd, runCmd, stdout=LOG, stderr=LOG)
    
        #daemon.start()
        if len(sys.argv) != 2:
            print('Usage: {} [start|stop]'.format(sys.argv[0]))
            raise SystemExit(1)
        if 'start' == sys.argv[1]:
            daemon.start()
        elif 'stop' == sys.argv[1]:
            daemon.stop()
        elif 'restart' == sys.argv[1]:
            daemon.restart()
        else:
            print('Unknown command {0}'.format(sys.argv[1]))
            raise SystemExit(1)
  • 相关阅读:
    [SCOI2009] Windy数
    [P1361] 小M的作物
    Wannafly Camp 2020 Day 2E 阔力梯的树
    2017百越杯反序列化writeup
    大美西安writeup
    Thinkphp的SQL查询方式
    Thinkphp的CURD
    记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)
    ThinkPHP的输出和模型使用
    ThinkPHP的运行流程-2
  • 原文地址:https://www.cnblogs.com/taurusfy/p/7941009.html
Copyright © 2020-2023  润新知