Python创建子进程,分离并退出

60 投票
3 回答
75774 浏览
提问于 2025-04-16 16:20

我在想,这样执行一个系统进程并让它和父进程分开,是否是正确的做法。这样做可以让父进程退出时,不会产生僵尸进程,也不会杀掉子进程。我现在使用的是subprocess模块,代码是这样的...

os.setsid() 
os.umask(0) 
p = subprocess.Popen(['nc', '-l', '8888'],
                     cwd=self.home,
                     stdout=subprocess.PIPE, 
                     stderr=subprocess.STDOUT)

os.setsid()这个函数可以改变进程组,我认为这就是让进程在父进程退出后还能继续运行的原因,因为它不再属于同一个进程组。

这样做对吗?这也是一种可靠的方法吗?

基本上,我有一个远程控制工具,它通过套接字进行通信,可以远程启动进程。但我必须确保,如果远程控制工具崩溃了,它启动的进程仍然能继续运行,不受影响。

我在阅读关于双重分叉的内容,但不确定这是否必要,或者subprocess.POpen的close_fds是否能处理这个问题,是否只需要改变进程组就可以了?

谢谢。

Ilya

3 个回答

1

在Windows上的解决方法:

    proc_exe = subprocess.Popen(<Your executable path>, shell=True)
    proc_exe.send_signal(subprocess.signal.SIGTERM)

当你使用subprocess.Popen()时,默认情况下,子进程的父进程就是调用它的那个进程。通常这是个好事,但在某些情况下,父进程的资源会被锁住,直到子进程结束。这个时候,可以用subprocess.send_signal()方法来解决问题。运行这个命令后,子进程就会和父进程断开连接,独立运行。

确保在调用子进程时使用“shell=True”选项。

因为send_signal()方法只会结束“CMD.exe”(父进程),而不是实际的调用者。
23

popen 在 Unix 系统上是通过 fork 来实现的。这意味着你可以安全地做以下两件事:

  1. 在你的父进程中运行 Popen
  2. 然后立即退出父进程

当父进程退出后,子进程会被 init 进程(在 OSX 上是 launchd)接管,并且会继续在后台运行。

你程序的前两行其实是可以省略的,下面的代码就可以正常工作:

import subprocess
p = subprocess.Popen(['nc', '-l', '8888'],
                     cwd="/",
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT)

我在阅读关于双重 fork 的内容,但不确定这是否必要。

如果你的父进程还在运行,而你又想保护子进程不跟着父进程一起结束,那就需要双重 fork。这个回答展示了如何实现。

双重 fork 的工作原理:

  1. 通过 os.fork() 创建一个子进程
  2. 在这个子进程中调用 Popen() 启动一个长时间运行的进程
  3. 子进程退出:Popen 进程会被 init 接管并在后台运行

为什么父进程必须立即退出?如果不立即退出会发生什么?

如果你让父进程继续运行,而用户通过 ctrl-CSIGINT)或 ctrl-\SIGQUIT)停止进程,那么父进程和 Popen 进程都会被杀掉。

如果它在 fork 后一秒才退出呢?

那么在这1秒内,你的 Popen 进程就会受到 ctrl-C 等信号的影响。如果你想确保万无一失,就需要使用双重 fork。

34

对于Python 3.8.x,处理方式稍微有点不同。可以使用从Python 3.2开始就有的start_new_session参数:

import shlex
import subprocess

cmd = "<full filepath plus arguments of child process>"
cmds = shlex.split(cmd)
p = subprocess.Popen(cmds, start_new_session=True)

这样做可以让父进程退出,而子进程继续运行。不过关于僵尸进程的情况就不太确定了。

start_new_session参数在所有POSIX系统上都支持,也就是说在Linux、MacOS等系统上都可以用。

这个是在macOS 10.15.5上测试的Python 3.8.1版本。

撰写回答