【问题标题】:Preventing threaded subprocess.popen from terminating my main script when child is killed?当孩子被杀死时,防止线程 subprocess.popen 终止我的主脚本?
【发布时间】:2013-02-27 09:44:01
【问题描述】:

Solaris 10 上的 Python 2.7.3

问题

  1. 当我的子进程存在内部分段错误(核心)问题或用户使用 SIGTERM 或 SIGKILL 从 shell 外部终止它时,我的主程序的信号处理程序会处理 SIGTERM(-15) 并且我的父程序退出。 这是真的吗?或者它是一个糟糕的 python 构建?

背景和代码

我有一个 python 脚本,它首先产生一个工作管理线程。工作者管理线程然后产生一个或多个工作者线程。我的主线程中还有其他无法阻止的事情。我的管理线程的东西和工作线程是坚如磐石的。我的服务运行了多年而没有重新启动,但是我们有这个subprocess.Popen 场景:

在工作线程的run方法中,我使用的是:

class workerThread(threading.Thread):
    def __init__(self) :
        super(workerThread, self).__init__()
    ...
    def run(self)
        ...
        atempfile = tempfile.NamedTempFile(delete=False)

        myprocess = subprocess.Popen( ['third-party-cmd', 'with', 'arguments'],  shell=False, stdin=subprocess.PIPE, stdout=atempfile, stderr=subprocess.STDOUT,close_fds=True)
        ...

我需要使用myprocess.poll() 来检查进程是否终止,因为我需要扫描atempfile 直到找到相关信息(文件可能> 1 GiB)并且我需要由于用户请求或终止进程因为进程运行时间太长了。一旦我找到我要找的东西,我将停止检查标准输出临时文件。我会在外部进程死后和工作线程终止之前清理它。我需要标准输入管道,以防我需要在孩子的标准输入流中注入对交互的响应。

在我的主程序中,我设置了一个 SIGINT 和 SIGTERM 处理程序来执行清理,如果我的主 python 程序以 SIGTERM 或 SIGINT(Ctrl-C) 终止(如果从 shell 运行)。

有没有人在线程中处理子信号的可靠 2.x 配方? ctypes sigprocmask 等

任何帮助将不胜感激。我只是在寻找“官方”食谱或BEST hack,如果有的话。

备注

我正在使用受限版本的 Python。我必须使用 2.7.3。第三方 cmd 是我没有源代码的程序 - 无法修改。

【问题讨论】:

  • 欢迎来到 SO。即使我没有给您答案,我还是建议您删除大部分“文字墙”。这可能会吓跑潜在的回答者。至少,把它分成几个部分。
  • 感谢您的回复。不知道如何编辑它并获得我想要传达的内容。
  • 我对您的问题进行了编辑,以帮助使其更具可读性。您不必更改您所写的内容,但将其分成小节会很有帮助。
  • 另外,为了帮助您让其他人对您的帖子感兴趣,我建议您阅读this post about asking good questionsthis post about writing great (attractive) titles.
  • 感谢您的帮助。我绝对是新手。我去看看帖子

标签: multithreading python-2.7 signals subprocess parent-child


【解决方案1】:

你的描述中有很多看起来很奇怪的东西。首先,你有几个不同的线程和进程。谁在崩溃,谁在接收 SIGTERM,谁在接收 SIGKILL 以及由于哪些操作?

第二:为什么你的父母会收到 SIGTERM ?它不能被隐式发送。有人直接或间接地向您的父进程调用 kill(例如,通过杀死整个父组)。

第三点:当您处理 SIGTERM 时,您的程序如何终止?根据定义,如果未处理,程序将终止。如果已处理,则不会终止。到底发生了什么?

建议:

    $ cat crsh.c
    #include <stdio.h>

    int main(void)
    {
        int *f = 0x0;

        puts("Crashing");
        *f = 0;
        puts("Crashed");
        return 0;
    }
    $ cat a.py

    import subprocess, sys

    print('begin')
    p = subprocess.Popen('./crsh')
    a = raw_input()
    print(a)
    p.wait()
    print('end')
    $ python a.py 
    begin
    Crashing
    abcd
    abcd
    end

这行得通。没有信号传递给父级。您是否在程序中隔离了问题?

如果问题是信号发送到多个进程:是否可以使用 setpgid 为子进程设置单独的进程组?

创建临时文件有什么原因吗?它是在您的临时目录中创建的 1 GB 文件。为什么不管道标准输出?

如果您确实确定需要在父程序中处理信号(例如,您为什么不尝试/除了 KeyboardInterrupt?):多线程程序的 signal() 未指定行为是否会导致这些问题(例如例如,将信号分派给不处理信号的线程)?

NOTES
     The effects of signal() in a multithreaded process are unspecified.

无论如何,尝试更准确地解释程序的线程和进程是什么,它们做什么,信号处理程序是如何设置的,为什么,谁在发送信号,谁在接收等等,等等,等等,等等等等。

【讨论】:

  • 是的。您的示例确实有效,但我的问题要复杂得多。感谢您指出这令人困惑。我进一步澄清了文本。如上所述,我的 popen 发生在一个线程中,实际上是线程中的一个线程。使用 setpgid 不起作用,因为我已经尝试过使用 preexec_fn,结果是一样的。管道标准输出可能会导致标准输出管道中超过 64k 块的数据大小出现死锁。使用管道等待/通信肯定会导致进程死锁,该进程喷出 1 GB 数据。使用信号线程肯定会导致我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
相关资源
最近更新 更多