+
80
-

python子进程如何守护父进程防止意外退出?

python子进程如何守护父进程防止意外退出?

网友回复

+
0
-

什么是守护进程?

守护进程是一种进程驻留内存的后台进程,它脱离终端控制,不受终端信号影响,即 Ctrl+C,通常守护进程用于周期性的执行某种任务或持续等待处理某些发生的事件。 python实现原理

程序调用 fork()函数后,内存中的程序会在克隆出一份,然后使父进程退出,只保留子进程。父进程就是手终端信号控制的,例如Ctrl+C

如果你不想使用 root 用户运行,还可以通过 setuid,setgid 改变子进程的运行用户和组。

也可以改变子进程的工作目录和文件创建掩码

与守护进程通信需要用到信号处理。

示例代码

#!/usr/bin/python3
# -*- coding: UTF-8 -*-
#================================================================================
# 这是一个演示日志切割的python程序
#================================================================================

import time,os,signal,sys,atexit
# from atexit import register

pidfile = "/tmp/netkiller.pid"
logdir = "/tmp"
logfile = logdir+"/netkiller.log"
logger = None
loop = True
job = True

# 保存进程ID
def savepid(pid):
    with open(pidfile, 'w') as f:
        f.write(str(os.getpid()))
    # 注册退出函数,进程退出时自动移除pidfile文件
    atexit.register(os.remove, pidfile)
# 从pidfile中读取进程ID
def getpid():
    pid = 0
    try:
        with open(pidfile, 'r') as f:
            pid = int(f.readline())
    except FileNotFoundError as identifier:
        print(identifier)
    # print(pid)
    return pid

# 创建日志
def createlog():
    global logger
    logger = open(logfile,mode="a+")
    return logfile
# 写入日志
def log(level,msg):
    logger.write(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()) + " "+level+" "+ msg +"\r\n")
    logger.flush()

# 信号处理
def signalHandler(signum,frame):
    global loop,job
    # print("SIGN ",str(signum));
    if signum == signal.SIGHUP :
        # 优雅重启
        job = False
        # print("WARN","优雅重启完毕\r\n")
    elif signum == signal.SIGINT:
        # 正常退出
        loop = False
        job = False
        # print("WARN","正常退出 \r\n")
    elif signum == signal.SIGUSR1:
        # 日志切割
        job = False
        dst = time.strftime(logdir + "/netkiller-%Y-%m-%d.%H-%M-%S.log",time.localtime());
        os.rename(logfile, dst)

def daemonize():
    global job 

    signal.signal(signal.SIGHUP, signalHandler)
    signal.signal(signal.SIGINT, signalHandler)
    signal.signal(signal.SIGUSR1, signalHandler)
    signal.alarm(5)

    pid = os.fork()
    sys.stdout.flush()
    sys.stderr.flush()
    if pid :
        sys.exit(0)
    # print(os.getpid())
    savepid(str(os.getpid()))
    while loop :
        createlog()
        log("WARN","Start!!!")
        while job:
            main()
            # log("DEBUG",str(loop) + "|" + str(job))
        log("WARN","Exit!!!")
        job = True
        logger.flush()
        logger.close()

def start():
    if os.path.isfile(pidfile) :
        print("error")
        sys.exit(1)
    else:
        daemonize()

def stop():
    try:
        os.kill(getpid(), signal.SIGINT) 
    except ProcessLookupError as identifier:
        print(identifier)
    # os.remove(pidfile)

def reloads():
    try:
        os.kill(getpid(), signal.SIGHUP) 
    except ProcessLookupError as identifier:
        print(identifier)

def logrotate():
    try:
        os.kill(getpid(), signal.SIGUSR1) 
    except ProcessLookupError as identifier:
        print(identifier)

def main():
    # 业务逻辑
    log("INFO","Hello world!!!")
    # 业务逻辑
    time.sleep(2)

def usage():
    print(sys.argv[0] + " start | stop | restart | reload | log")

if __name__ == "__main__":
    # print(sys.argv)
    if len(sys.argv) > 1:
        arg = sys.argv[1]
        if arg == "start" :
            start()
        elif arg == "stop":
            stop()
        elif arg == "restart" :
            stop()
            start()    
        elif arg == "reload":
            reloads()
        elif arg == "log":
            logrotate()    
        else:
            usage()
    else: 
        usage()

我知道答案,我要回答