如何使用独立的标准输出、标准和标准配置来分叉新进程



我已经阅读了大多数关于子进程和os.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)
在这里,子进程

不再与父进程一起死亡,但在父进程死亡后,子进程无法再读取输入和写入输出。

因此,我想知道我如何使用完全独立的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外壳,它的工作原理与我开始时的 bash 外壳完全相同。
  3. 将创建文件/tmp/FooBar
  4. 原始 Python 脚本完成。
  5. 继续使用我闪亮的新 bash shell,ps aux | grep python 的输出不包括我刚刚运行的 python 脚本。

您的第一个示例对 Popen() 进行了额外的不必要的调用,因为调用便利函数只会在 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/FooBarn")
p.stdin.write("date >> /tmp/FooBarn")
p.terminate()

如果你还想控制输出,那么你将stdout和stderr重定向到PIPE(即 stdout = subprocess.PIPEstderr = 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)

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

$ echo 'test' > /tmp/pipe

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

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

Popen 根据文档支持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 相同的操作。

最新更新