Skip to main content
 首页 » 编程设计

python之从 Python 启动守护进程,然后将父进程与子进程分离

2025年05月04日8zengkefu

我在远程机器上运行一个守护进程:mydaemon。该守护进程应始终持续运行。

当我在远程机器上运行作业时,它还会启动一个轻量级 Python 服务器进程 my_remote_server.py。

我可以发送到 my_remote_server.py 的命令之一是重新启动 mydaemon,我正在尝试这样做:

os.system("killall mydaemon") 
subprocess.Popen(["mydaemon"], stdin=None, stdout=None, stderr=None, close_fds=True) 

当我的工作结束时,my_remote_server.py 应该终止,但 mydaemon 应该继续运行。但是我看到 my_remote_server.py 作为僵尸进程卡住了(这导致系统看不到我的工作已终止)

  820 root            Z   [my_remote_serve] 
  834 root        552 S   /usr/sbin/telnetd -l /bin/sh  
  835 root        836 S   /bin/sh  
  844 root        672 S   mydaemon  

我想将父级 (my_remote_server.py) 与子级 (mydaemon) 分离,但我不知道该怎么做。

-- 我的python版本是2.5.4

编辑:

我想我现在对守护进程有了更好的理解,但我仍然无法让守护进程分离

为简洁起见,我在这里省略了错误处理

os.system("killall mydaemon") 
 
if(os.fork() > 0): 
    return True # my_remote_server.py returns to handle additional commands 
 
os.setsid() 
if(os.fork() > 0): 
    exit(0) # first child exits after becoming session leader 
 
os.execlp("mydaemon") # have the second child run as the daemon 

这是我在调用 restart_mydaemon 函数之前的 ps 列表

252 root        672 S   mydaemon 
286 root       4552 S   /usr/bin/python my_remote_server.py 

这是在restart_mydaemon 之后,第一个 child 僵尸化了(它不应该消失了吗?)

286 root       4552 S   /usr/bin/python my_remote_server.py 
300 root            Z   [my_remote_serve] 
304 root        672 S   [mydaemon] 

这是作业终止的时候(my_remote_server.py 应该已经退出,但它是一个僵尸,然而,此时第一个 child 已经退出)

286 root            Z   [my_remote_serve] 
304 root       1012 S   [mydaemon] 

请您参考如下方法:

将一个进程变成一个守护进程是一个多步骤的过程。您的部分问题是您的父进程没有正确等待子进程终止(阅读 wait/waitpid),

  • 您的进程必须关闭所有打开的文件描述符(stdin、stdout、stderr)
  • 您的进程需要(更改目录、设置 umask,无论您想要/需要什么)
  • 你必须 fork 一个进程
  • 该流程必须成为流程组领导
  • 您必须再次 fork ,以完全脱离(祖) parent

    try: 
        #free parent, detach from process group 
        pid = os.fork() 
        if( pid>0 ): 
            exit(0) #parent exits 
    except OSError, e: 
        raise Exception("%s [%d]" % (e.strerror, e.errno)) 
    #become session leader, process group leader, detach from controlling terminal 
    os.setsid() 
    try: 
        #prevent zombie process, make init cleanup 
        pid = os.fork() 
        if( pid>0 ): 
            exit(0) #parent exits 
    except OSError, e: 
        raise Exception("%s [%d]" % (e.strerror, e.errno)) 
    #change directories, close stdin/out/err, etc 
    os.chdir(MYDIR) 
    os.umask(MYMASK) 
    #close files (including stdin, stdout, stderr) 
    #(re)open new stdin/stdout, if desired