【问题标题】:Python - Windows - Exiting Child Process when "unrelated" parent dies/crashesPython - Windows - 当“不相关”的父进程死亡/崩溃时退出子进程
【发布时间】:2018-05-19 11:10:15
【问题描述】:

经过一些研究,对于如何让子进程了解父进程在 Windows 下已经死亡/崩溃/退出,这可能使子进程在无人看管的情况下运行,没有明确的答案。有一些建议如下:

总是涉及一个已经生了孩子的已知父母。但也有孩子不知道自己是孩子的情况,因为它没有被认为是孩子,父母也没有努力杀死孩子。

此外,没有对父级的控制。实际案例:

  • 在 Windows 下运行的 Cygwin
  • Windows Python 路径中的第一个
  • Python 可执行文件通过setuptools entry_points 工具安装。

如上所述,要执行的 Python 是 Windows 的。 setuptools 生成的可执行文件将找到它并使用相关脚本将其作为子进程执行。

因为是在 Cygwin 下运行的,所以以下可能会失败:

  • Ctrl-c 将杀死父级(存根 setuptools 可执行文件)
  • 但会让子进程继续运行(在进程列表中以python.exe 的形式找到)

在这种情况下,如上所述,无法控制父级并且子级不知道自己是子级(因为它也可能直接作为 Python 脚本执行)

【问题讨论】:

  • setuptools EXE 包装器有缺陷且已过时。只要有可能,构建/安装一个 wheel 包,它 pip 使用来自 distlib 的 EXE 存根安装。这些启动器使用 Job 对象来确保子进程与父进程一起终止。
  • pip install -e .

标签: python windows python-3.x subprocess


【解决方案1】:

解决方法如下

import sys

def win_wait_for_parent(raise_exceptions=False):
    if not sys.platform == 'win32':
        return True

    # When started under cygwin, the parent process will die leaving a child
    # hanging around. The process has to be waited upon
    import ctypes
    from ctypes.wintypes import DWORD, BOOL, HANDLE
    import os
    import threading

    INFINITE = -1
    SYNCHRONIZE = 0x00100000

    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

    kernel32.OpenProcess.argtypes = (DWORD, BOOL, DWORD)
    kernel32.OpenProcess.restype = HANDLE

    kernel32.WaitForSingleObject.argtypes = (HANDLE, DWORD)
    kernel32.WaitForSingleObject.restype = DWORD

    phandle = kernel32.OpenProcess(SYNCHRONIZE, 0, os.getppid())

    def check_parent():
        # Get a token with right access to parent and wait for it to be
        # signaled (die). Exit ourselves then
            kernel32.WaitForSingleObject(phandle, INFINITE)
            os._exit(0)

    if not phandle:
        if raise_exceptions:
            raise ctypes.WinError(ctypes.get_last_error())

        return False

    threading.Thread(target=check_parent).start()
    return True

如果进程的 PID 与等待父进程发出信号(死亡)的父进程的 PID 不同,则在单独的线程中运行。这适用于 Python 3.3,其中 os.getppid() 实际上返回窗口下父级的 PID

它不需要对父级进行修改,也不需要将子级提前编码为子级,因为检查了线程必须在哪里运行。

-- 重构为一个函数并添加了来自 cmets 的改进

【讨论】:

  • 如果没有在辅助线程中获取父级句柄并且所有内容都包装在一个函数中,那么引发异常才有意义,恕我直言。这一切都说得通,但代码是作为一个 sn-p 的。
猜你喜欢
  • 2014-06-19
  • 1970-01-01
  • 2012-12-17
  • 2010-09-24
  • 2014-09-16
  • 2013-10-27
  • 1970-01-01
相关资源
最近更新 更多