如何使用独立的stdout、stderr和stdin分叉一个新进程?

6

我已经阅读了关于子进程和os.fork()的大部分相关问题,包括有关双重fork技巧的所有讨论。然而,这些解决方案似乎都不适用于我的情况。

我想要fork一个新进程,并允许父进程正常终止,而不会破坏子进程的标准输入、标准输出和标准错误,并且不会杀死子进程。

我的第一次尝试是使用subprocess.Popen()

#!/usr/bin/python

from subprocess import call,Popen

Popen("/bin/bash", shell=True)
call("echo Hello > /tmp/FooBar", shell=True)

这里的问题是当父进程退出时,子进程也会被终止。我知道有creationflags,但它只适用于Windows系统,而我正在使用Linux。如果我们在父进程末尾添加一个无限循环来保持父进程存活,则上述代码可以完美运行。然而,这并不理想,因为父进程已经完成了其工作,没有什么实际原因让它继续存在。
第二次尝试是使用os.fork()
#!/usr/bin/python

from subprocess import call
from os import fork


try: 
  pid = fork()
  if pid > 0: 
    pass    
  else: # child process will start interactive process
    call("/bin/bash", shell=True)
except:
  print "Forking failed!"

call("echo Hello > /tmp/FooBar", shell=True)

在这里,子进程不再随父进程死亡,但在父进程死亡后,子进程将无法再读取输入和写入输出。

因此,我想知道如何完全独立地fork一个新进程,其中包括 stdoutstderrstdin。独立性意味着父进程可以(正常)终止,而子进程(无论是 bash 还是 tmux 或任何其他交互式程序)表现得就像父程序尚未终止一样。更准确地说,考虑以下原始程序的变体。

#!/usr/bin/python

from subprocess import call,Popen

Popen("/bin/bash", shell=True)
call("echo Hello > /tmp/FooBar", shell=True)
while True:
   pass

上述代码具备我所需的所有功能,但却会人为地让Python进程保持活动状态。我正试图实现这种行为,而不让Python进程保持活动状态注意:我是通过ssh运行这些应用程序,因此产生一个新的GUI窗口不可行。 期望的行为
  1. 运行Python代码。
  2. 获得一个全新的bash shell,其工作方式与我最初启动的bash shell完全相同。
  3. 创建文件/tmp/FooBar
  4. 原始Python脚本完成。
  5. 继续使用我的全新的bash shell,而ps aux | grep python的输出不包括我刚刚运行的Python脚本。

无法使用独立线程执行 "tmux -S /tmp/TestSmux attach-session" 的命令吗?此线程不能是守护进程,否则将得不到预期的结果。您可以使这个线程的RUN方法一直运行直到任务完成,这样您的父进程就可以继续工作,并且您可以根据子进程中接收到的输入,在父进程中运行一些代码来发出信号(事件)。然后,您可以从父进程发出信号来停止子进程,或者加入并确保父进程和子进程都停止。 - Syed Mauze Rehan
  1. tmux是一个复杂的例子。如果你需要 tmux,请询问你遇到的具体问题。
  2. 你的问题是:如何在Python中模拟nohupdisown或创建一个适当的守护进程?
- jfs
@SyedMauzeRehan,我已经给你提供了完整的可重现示例和我的行为观察。它的行为类似于守护进程,但它是一个守护进程吗?我不能确定。 - merlin2011
@J.F.Sebastian,请按照问题的原文进行回答。如果我将“tmux attach-session”替换为“vim”,并用其他某个命令替换“vim”后面的命令,然后退出,那么问题就一样了。关于您澄清的问题,我试图模拟它们中的任何一个或所有这些问题,具体取决于它们是否实现了我所描述的行为--保持交互式会话,运行更多代码,然后在父进程中退出而不给子进程带来麻烦。 - merlin2011
@J.F.Sebastian,如果您的担忧是我的示例因为tmux而变得比必要的复杂,那么这是完全合理的批评。当我回到电脑时,我会编辑问题。我最初使用这个特定的MWE是因为它恰好是我试图解决的问题,而不是因为我认为这个问题不是普遍存在的。 - merlin2011
显示剩余13条评论
2个回答

0

你的第一个示例中多了一个不必要的 Popen() 调用,因为 call 方便函数将只在 shell 中执行其命令并退出,所以如果你只运行以下命令,它就可以工作:

from subprocess import call, Popen

call("echo Hello > /tmp/FooBar", shell=True)

然而,如果你想要向一个shell发送一系列指令,那么这个shell就需要使用stdin连接到管道上打开,这样它就不会与父进程的stdin混淆(从效果上来看,这就是你所期望的获得独立的stdio):

p = Popen("/bin/bash", shell=True, stdin = subprocess.PIPE)
p.stdin.write("echo Hello > /tmp/FooBar\n")
p.stdin.write("date >> /tmp/FooBar\n")
p.terminate()

如果你想控制输出,你可以将 stdout 和 stderr 重定向到 PIPE(即 stdout = subprocess.PIPE, stderr = subprocess.PIPE), 然后调用 p.stdout.read() 来获取所需的输出。
为了让进程在退出 python 后继续运行,可以在命令末尾添加 & 操作符使其在后台继续运行,例如:
call("nc -l 2000 > /tmp/nc < &",shell=True)

如果要运行一个进程,使得stdin和stdout仍保持连接,可以使用shell重定向。为了保持对stdin的访问,可以使用mkfifo创建一个命名管道,例如:

call("mkfifo /tmp/pipe",shell=True)
call("tail -f /tmp/pipe > /tmp/out &",shell=True)

要提供标准输入stdin,只需将数据发送到管道,例如从shell:

$ echo 'test' > /tmp/pipe

这个答案没有实现我在问题中所描述的行为。请阅读问题的最后一部分。 - merlin2011
我已经更新了我的回答 - 如果你将进程放到后台,那么它会在Python退出后继续运行。 - Pierz
这并没有解决维护子进程交互式stdin的问题。 - merlin2011
我已经添加了另一个更新,以解释如何使用管道来维护对stdin的访问。 - Pierz
这些都是有用的技巧,但它们忽略了主要问题:维护一个与任何其他shell完全相同的交互式shell。 - merlin2011
子进程 shell 命令可以通过连接到 stdin/out 的 PIPE 进行交互(并且在 Python 退出后不会被终止),但我猜如果您想要完全交互式访问 shell,则需要使用 screen/tmux 或复制该功能。 - Pierz

0

我最近遇到了这个问题,我找到了一个可能有用的解决方案, 使用creationflags告诉Popen子进程不应该继承父进程控制台,因此它有自己的stdout、stdin和stderr,就像它是一个父进程一样。

subprocess.Popen("/bin/bash", creationflags= subprocess.CREATE_NEW_CONSOLE)

根据文档,Popen支持creationflags关键字参数:

如果给定creationflags,则可以是以下标志之一或多个:

CREATE_NEW_CONSOLE

CREATE_NEW_PROCESS_GROUP

ABOVE_NORMAL_PRIORITY_CLASS

BELOW_NORMAL_PRIORITY_CLASS

HIGH_PRIORITY_CLASS

IDLE_PRIORITY_CLASS

NORMAL_PRIORITY_CLASS

REALTIME_PRIORITY_CLASS

CREATE_NO_WINDOW

DETACHED_PROCESS

CREATE_DEFAULT_ERROR_MODE

CREATE_BREAKAWAY_FROM_JOB

你感兴趣的是DETACHED_PROCESSCREATE_NEW_CONSOLE,我使用了CREATE_NEW_CONSOLE,它会像父进程一样生成一个新进程。我在Python3.5 Windows上使用,在Python 3.7中增加了DETACHED_PROCESS,它被记录为与CREATE_NEW_CONSOLE做相同的事情。


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