【问题标题】:Python: How to determine subprocess children have all finished runningPython:如何确定子进程子进程已全部运行完毕
【发布时间】:2011-08-31 10:42:16
【问题描述】:

我正在尝试检测安装程序何时从 Python 脚本中完成执行。具体来说,该应用程序是 Oracle 10gR2 数据库。目前我正在使用带有 Popen 的子进程模块。理想情况下,我会简单地使用 wait() 方法来等待安装完成执行,但是,记录的命令实际上会产生子进程来处理实际安装。下面是一些失败代码的示例代码:

import subprocess
OUI_DATABASE_10GR2_SUBPROCESS = ['sudo',
                                 '-u',
                                 'oracle',
                                 os.path.join(DATABASE_10GR2_TMP_PATH,
                                              'database',
                                              'runInstaller'),
                                 '-ignoreSysPrereqs',
                                 '-silent',
                                 '-noconfig',
                                 '-responseFile '+ORACLE_DATABASE_10GR2_SILENT_RESPONSE]
oracle_subprocess = subprocess.Popen(OUI_DATABASE_10GR2_SUBPROCESS)
oracle_subprocess.wait()

这里有一个类似的问题:Killing a subprocess including its children from python,但是选择的答案并没有解决儿童问题,而是指示用户直接调用应用程序等待。我正在寻找一个特定的解决方案,它将等待子进程的所有子进程。如果有未知数量的子流程怎么办?我将选择解决等待所有子子流程完成问题的答案。

更清楚地说明失败:子进程在 wait() 命令之后继续执行,因为该命令只等待顶级进程(在本例中为“sudo”)。这是此问题中已知子进程的简单图: Python 子进程模块 -> Sudo -> runInstaller -> java -> (未知)

【问题讨论】:

  • 我只是在问题的底部添加了更多的清晰度。
  • 你能阻止oracle产生子进程吗?
  • 不,我不明白oracle安装程序的流程。我正在寻找一个可以应用于任何进程子生成问题的通用答案。
  • sudo 应该等待它的子进程完成。 runInstaller 不等待它的孩子吗?这对我来说似乎很奇怪。如果属实,那么您无能为力(至少在 Unix 风格的操作系统上)。孤立进程的父进程将成为 init 进程(pid 1)。
  • 我认为 sudo 正在等待其直接子进程完成(一个名为 runInstaller 的 bash 脚本),但该子进程正在调用 java.util. runInstaller 脚本不会等待 java 完成执行,而是在所有子进程完成之前完成。

标签: python oracle10g subprocess child-process spawning


【解决方案1】:

查看以下链接http://www.oracle-wiki.net/startdocsruninstaller,其中详细介绍了可用于 runInstaller 命令的标志。

这个标志绝对适用于 11gR2,但我没有一个 10g 的数据库来为与该版本打包的 runInstaller 试用这个标志。

问候

【讨论】:

  • +1 谢谢马克。我会接受这个作为答案,但这个问题对于所有子流程都是通用的,所以我不希望其他人都觉得被骗了。
【解决方案2】:

我所看到的每一个地方似乎都在说在一般情况下不可能解决这个问题。我创建了一个名为“pidmon”的库,它结合了一些适用于 Windows 和 Linux 的答案,可能会满足您的需求。

我打算把它清理干净并放在 github 上,可能叫做 'pidmon' 或类似的东西。如果/当我启动它时,我会发布一个链接。

编辑:可在http://github.com/dbarnett/python-pidmon 获得。

我制作了一个特殊的 waitpid 函数,它接受 graft_func 参数,以便您可以松散地定义当它们不是直接子代时要等待的进程类型:

import pidmon
pidmon.waitpid(oracle_subprocess.pid, recursive=True,
        graft_func=(lambda p: p.name == '???' and p.parent.pid == ???))

或者,作为一种霰弹枪方法,只需等待自调用 waitpid 后启动的任何进程再次停止,请执行以下操作:

import pidmon
pidmon.waitpid(oracle_subprocess.pid, graft_func=(lambda p: True))

请注意,这仍然几乎没有在 Windows 上进行测试,并且在 Windows 上看起来很慢(但我是否提到它在 github 上很容易分叉?)。这至少应该让你开始,如果它对你有用,我有很多关于如何优化它的想法。

【讨论】:

  • 别搞错了,这是一个 hack,而不是优雅的“问题解决方案”,但我认为它可能对某些人有所帮助。
  • 另外,我知道os.waitpid,我只是用不是python进程的子进程来测试它,而AFAIK waitpid 只适用于当前进程的子进程。我可能会尝试使用 waitpid 并回退到这种手动方法。
  • 我喜欢你在 Github 上快速发布的方式。我会尝试一下,看看它是否有效。我认为这里的主要问题是从 shell 脚本生成的进程的行为不同。希望它有效,会尽快回复您!
  • @pokstad:是的,Github 是让代码“真实”、社交和在互联网上随时可用的绝佳酱料。我尽量利用它。
  • @pokstad:我注意到,当我在 Linux 上测试它时,例如,如果我从 gnome-terminal 内部运行 gnome-terminal,它会通过“工厂”间接打开它,所以它搞砸了进程层次结构。你的问题可能类似。我不确定有什么可做的,但我正在考虑我可以包括哪些类型的黑客,以便它看起来“贪婪”地用于后代进程并捕获有趣的产卵。顺便说一句,还没有包含依赖项的 setup.py 文件,但显然 win32com 是在 Windows 上使用它的一个 dep。
【解决方案3】:

您可以使用os.waitpid 并将 pid 设置为 -1,这将等待当前进程的所有子进程直到它们完成:

import os
import sys
import subprocess


proc = subprocess.Popen([sys.executable,
                         '-c',
                         'import subprocess;'
                         'subprocess.Popen("sleep 5", shell=True).wait()'])

pid, status = os.waitpid(-1, 0)

print pid, status

这是pstree <pid> 不同子进程分叉的结果:

python───python───sh───sleep

希望这可以帮助:)

【讨论】:

  • 这是一种让它工作的非常有趣的方法,但它假设您可以拦截每个正在生成的子进程。为了解决我的问题,我必须拆除正在运行的实际 Java 程序并在 Java 中插入与此代码等效的代码(我不能这样做,因为我没有源代码)。我同意,如果我只有 Python 可执行文件和最底部的 shell 脚本,这将起作用,但不幸的是,我正在处理我无法修改的代码。
  • @pokstad:我不确定你的意思是:but it assumes that you can intercept each child process being spawned,但在同样的情况下,我不确定它是否适用于你的情况,我没有像你这样的真实案例在我手中进行测试:),希望这至少有帮助:)
  • 是的,它很有帮助,感谢您的帮助。我所说的“拦截”是因为您的示例有效的原因是因为您示例中的每个进程都有代码等待其子进程完成。我要等待的可执行文件没有此代码,否则它将起作用。所以为了让我的工作像你的一样,我需要在其中插入一些“等待”代码,这是我无法做到的。
  • @pokstad :是的,这是真的,没关系,希望你得到你正在寻找的东西 :)
【解决方案4】:

好的,这是一个只能在 Unix 下工作的技巧。它类似于此问题的答案之一:Ensuring subprocesses are dead on exiting Python program。这个想法是创建一个新的进程组。然后,您可以等待组中的所有进程终止。

pid = os.fork()
if pid == 0:
    os.setpgrp()
    oracle_subprocess = subprocess.Popen(OUI_DATABASE_10GR2_SUBPROCESS)
    oracle_subprocess.wait()
    os._exit(0)
else:
    os.waitpid(-pid)

我没有测试过这个。它创建了一个额外的子进程作为进程组的领导者,但避免这样做(我认为)要复杂得多。

我发现这个网页也很有帮助。 http://code.activestate.com/recipes/278731-creating-a-daemon-the-python-way/

【讨论】:

  • 我会试一试,然后再告诉你结果。
  • 仍然失败,可能与 shell 脚本正在执行 Java 程序有关,但我不知道为什么。
猜你喜欢
  • 1970-01-01
  • 2021-11-08
  • 2011-04-13
  • 2013-08-11
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多