【问题标题】:Python losing control of subprocess?Python失去对子进程的控制?
【发布时间】:2012-10-08 02:00:11
【问题描述】:

我正在使用一个商业应用程序,它使用 Python 作为其脚本 API 的一部分。提供的功能之一是称为App.run()。当这个函数被调用时,它会启动一个新的 Java 进程来完成其余的执行。 (不幸的是,我真的不知道它在幕后做了什么,因为提供的 Python 模块是 .pyc 文件,并且许多 Python 函数是 SWIG 生成的)。

我遇到的麻烦是我正在将App.run() 调用构建到一个更大的 Python 应用程序中,该应用程序需要执行一些有保证的清理代码(关闭数据库等)。不幸的是,如果子进程被 Ctrl+C 中断,它会中止并返回命令行,而不会将控制权返回给 Python 主程序。因此,我的清理代码永远不会执行。

到目前为止我已经尝试过:

  1. 用 atexit... 注册函数不起作用
  2. 将清理放入类__del__ 析构函数...不起作用。 (App.run() 在课堂内)
  3. 在 Python 主应用程序中为 Ctrl+C 创建信号处理程序...不起作用
  4. App.run() 放入线程中...导致Ctrl+C 后出现内存故障
  5. App.run() 放入进程中(来自多处理)...不起作用

任何想法可能会发生什么?

【问题讨论】:

  • 欢迎来到 Stack Overflow。我已经编辑了您的问题以使代码更加可见。
  • 我是在 Windows 还是 Linux 上运行的?

标签: python linux subprocess


【解决方案1】:

如果try: App.run() finally: cleanup() 不起作用;您可以尝试在子进程中运行它:

import sys
from subprocess import call

rc = call([sys.executable, 'path/to/run_app.py'])
cleanup()

或者,如果您有字符串中的代码,您可以使用-c 选项,例如:

rc = call([sys.executable, '-c', '''import sys
print(sys.argv)
'''])

您可以通过添加使用子流程来实施@tMC​​ 的建议 preexec_fn=os.setsid 参数(注意:没有 ())虽然我不知道创建一个进程组在这里有什么帮助。或者您可以尝试 shell=True 参数在单独的 shell 中运行它。

你可以再试一次多处理:

import multiprocessing as mp

if __name__=="__main__":
   p = mp.Process(target=App.run)
   p.start()
   p.join()
   cleanup()

【讨论】:

  • os.setsid 确实是一种习惯力量。没有它就值得测试。
  • @tMC 看到我上面的评论.. 似乎 setsid 确实有所作为。
  • 我尝试通过动态生成 Python 脚本并调用它来执行 subprocess.call 方法。由于某种原因,它没有运行,即使它的返回码为 0。如果我从命令行(或 Python 交互式)运行动态生成的文件,它可以正常工作。这种奇怪的行为......
  • @jasonm76:我已经更新了答案,以阐明如何将 Python 脚本作为子进程运行。
  • 有效!我只是将调用命令包装在try..except KeyboardInterrupt 语句中,Ctrl-C 导致子进程优雅地退出,然后是我的清理代码。谢谢!!
【解决方案2】:

这只是一个大纲-但是是这样的吗?

import os

cpid = os.fork()
if not cpid:
    # change stdio handles etc
    os.setsid() # Probably not needed
    App.run()
    os._exit(0)

os.waitpid(cpid)
# clean up here

(os.fork 仅适用于 *nix)

同样的想法可以用subprocess 以与操作系统无关的方式实现。思路是在子进程中运行App.run(),然后等待子进程退出;无论子进程如何死亡。在 posix 上,您还可以捕获 SIGCHLD(子进程死亡)。我不是 Windows 大师,所以如果适用并且 subprocess 不起作用,其他人将不得不在这里插话。

App.run() 被调用后,我很好奇进程树是什么样子的。它有可能运行exec 并接管python 进程空间。如果发生这种情况,创建子进程是我能想到的捕获它的唯一方法。

【讨论】:

  • 这似乎接近可行的方法。当我尝试它时,我可以按 Ctrl-C 并进入清理代码,但 App.Run() 进程不断重新启动!我对 *nix 命令不够熟悉,不知道发生了什么。
  • 如果你没有在你的python代码中放置任何循环;循环必须在 App.run() 代码中。应用运行时,调用它的python进程是否还存在? ps fax 将打印进程树。如果没有,应用程序代码调用exec 并占用了您的python 进程空间。如果发生这种情况,您对应用程序的功能几乎没有追索权或影响力。 (没有发送信号)
  • ps fax 命令非常有用。据我所知,如果我不使用setsid,当我点击Ctrl-C 时,App 对象会生成消息,表明它在中止之前收到了两个停止信号。当我使用setsid 时,Ctrl-C 会杀死 Python 线程,并且分叉的线程会自行运行,但它无法再接收 Ctrl-C
【解决方案3】:

您能否将 App.Run() 包装在 Try/Catch 中?

类似:

try:
    App.Run()
except (KeyboardInterrupt, SystemExit):
    print "User requested an exit..."
cleanup()

【讨论】:

  • 谢谢,我刚刚尝试了您的代码...不幸的是,它与我的其他测试结果相同:立即退出到命令行,没有打印任何内容。
  • 无赖,我认为这对你有用。你的 App.Run() 必须做一些令人讨厌的事情来降低 Python 进程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 2018-05-28
  • 1970-01-01
  • 1970-01-01
  • 2019-01-31
  • 1970-01-01
相关资源
最近更新 更多