Python生成一个子进程,分离并退出。

57

我想知道这是否是执行系统进程并从父进程分离的正确方式,同时允许父进程退出而不创建僵尸进程和/或杀死子进程。我目前正在使用subprocess模块,并进行以下操作...

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

os.setsid()函数会改变进程组,这也就是当父进程退出时让子进程继续运行的原因,因为它不再属于同一进程组。

这种做法可靠吗?

基本上,我有一个远程控制工具通过套接字通信并允许远程启动进程,但我必须确保如果远程控制死亡,它启动的进程可以继续正常运行。

我正在阅读关于双重forks的文章,不确定是否需要这样做,或者subprocess.POpen close_fds是否可以解决问题,只需要改变进程组吗?

谢谢。

Ilya


请查看以下链接: http://code.activestate.com/recipes/278731-creating-a-daemon-the-python-way/ http://www.python.org/dev/peps/pep-3143/#reference-implementation - sehe
2个回答

31

对于Python 3.8.x,过程略有不同。使用自Python 3.2起可用的start_new_session参数:

转化后:

对于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。


3
我可以确认在Python 3.7及自Python 3.2以来,start_new_session都是有效的。在我看来,这是最清晰的方法,至少对我而言是如此。 - SLAVA
我应该澄清一下,它在Debian 10和Python 3.7.3中对我有效。 - SLAVA
3
start_new_session=True 的效果非常好。如果你想抑制输出,可以添加这些参数:stdout=subprocess.DEVNULLstderr=subprocess.STDOUT - Saurav Pathak
Python 3.8及以上版本中最干净的方法是什么? - Vishwanath Heddoori
@VishwanathHeddoori,虽然回复有点晚了,但是看一下detach.call - Alex Peters

22

popen在Unix上使用fork完成。这意味着您可以安全地执行以下操作:

  1. 在父进程中运行Popen
  2. 立即退出父进程

当父进程退出时,子进程将被init进程(在OSX上的launchd)继承并将在后台继续运行。

您的Python程序的前两行是不需要的,以下代码完全可行:

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

我正在阅读关于双重fork的内容,不确定是否有必要。如果您的父进程保持运行并且需要保护子进程免于与父进程一起停止,则需要这个。此答案展示了如何实现。双重fork的工作原理如下:
1. 通过os.fork()创建一个子进程。 2. 在该子进程中调用Popen()来启动长时间运行的进程。 3. 子进程退出:Popen进程被init继承并在后台运行。
如果您让父进程继续运行,并且用户停止了进程(例如通过ctrl-C (SIGINT)或ctrl-\ (SIGQUIT)),则会同时杀死父进程和Popen进程。如果它在fork之后一秒钟退出会发生什么?

然后,在这1秒的期间,您的 Popen 进程容易受到 ctrl-c 等的攻击。如果您需要百分之百地确定,请使用双重派生。


1
你能解释一下为什么父进程必须立即退出吗?如果不立即退出会发生什么?同时,“立即”是什么意思?如果它在fork之后一秒钟退出会怎样? - Hubro
@Hubro:我已经将你的问题的答案添加到我的回答中,并且还澄清了一些解释不清的地方。 - hansaplast
4
如果子进程向管道中输出大量内容,该过程将会挂起。如果您不想与该进程通信,请将stdout=None。有关更多详细信息,请参见:https://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/ - Dr_Zaszuś

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接