Python:如何在父进程死亡时杀死子进程?

Python:如何在父进程死亡时杀死子进程?,python,linux,windows,subprocess,kill,Python,Linux,Windows,Subprocess,Kill,子进程开始于 subprocess.Popen(arg) 有没有办法确保在父级异常终止时终止它?我需要这个在Windows和Linux上都能工作。我知道这一点 编辑: 如果存在使用不同启动流程方法的解决方案,则可以放宽使用子流程.Popen(arg)启动子流程的要求。Popen对象提供终止和终止方法 这些为您发送SIGTERM和SIGKILL信号。 您可以执行以下类似操作: from subprocess import Popen p = None try: p = Popen(a

子进程开始于

subprocess.Popen(arg)
有没有办法确保在父级异常终止时终止它?我需要这个在Windows和Linux上都能工作。我知道这一点

编辑:


如果存在使用不同启动流程方法的解决方案,则可以放宽使用
子流程.Popen(arg)
启动子流程的要求。

Popen对象提供终止和终止方法

这些为您发送SIGTERM和SIGKILL信号。 您可以执行以下类似操作:

from subprocess import Popen

p = None
try:
    p = Popen(arg)
    # some code here
except Exception as ex:
    print 'Parent program has exited with the below error:\n{0}'.format(ex)
    if p:
        p.terminate()
更新:

您是对的——上面的代码不会防止硬崩溃或有人杀死您的进程。在这种情况下,您可以尝试将子进程包装到类中,并使用轮询模型来监视父进程。 请注意psutil是非标准的。

import os
import psutil

from multiprocessing import Process
from time import sleep


class MyProcessAbstraction(object):
    def __init__(self, parent_pid, command):
        """
        @type parent_pid: int
        @type command: str
        """
        self._child = None
        self._cmd = command
        self._parent = psutil.Process(pid=parent_pid)

    def run_child(self):
        """
        Start a child process by running self._cmd. 
        Wait until the parent process (self._parent) has died, then kill the 
        child.
        """
        print '---- Running command: "%s" ----' % self._cmd
        self._child = psutil.Popen(self._cmd)
        try:
            while self._parent.status == psutil.STATUS_RUNNING:
                sleep(1)
        except psutil.NoSuchProcess:
            pass
        finally:
            print '---- Terminating child PID %s ----' % self._child.pid
            self._child.terminate()


if __name__ == "__main__":
    parent = os.getpid()
    child = MyProcessAbstraction(parent, 'ping -t localhost')
    child_proc = Process(target=child.run_child)
    child_proc.daemon = True
    child_proc.start()

    print '---- Try killing PID: %s ----' % parent
    while True:
        sleep(1)

在本例中,我运行了“ping-t localhost”b/c,它将永远运行。如果杀死父进程,子进程(ping命令)也将被杀死

嘿,我昨天自己也在研究这个!假设您无法更改子程序:

在Linux上,
prctl(PR\u SET\u PDEATHSIG,…)
可能是唯一可靠的选择。(如果绝对有必要终止子进程,那么您可能希望将死亡信号设置为SIGKILL而不是SIGTERM;您链接的代码使用SIGTERM,但如果子进程愿意,它可以选择忽略SIGTERM。)

在Windows上,最可靠的选择是使用。其思想是创建一个“作业”(一种进程容器),然后将子进程放入作业中,然后设置一个神奇的选项,即“当没有人持有此作业的“句柄”时,然后杀死其中的进程”。默认情况下,作业的唯一“句柄”是父进程持有的句柄,当父进程死亡时,操作系统将检查并关闭其所有句柄,然后注意,这意味着作业没有打开的句柄。于是它按要求杀死了孩子。(如果您有多个子进程,可以将它们全部分配给同一个作业。)使用
win32api
模块提供了执行此操作的示例代码。该代码使用
CreateProcess
来启动子进程,而不是
subprocess.Popen
。原因是他们需要为生成的子进程获取一个“进程句柄”,默认情况下,
CreateProcess
会返回该句柄。如果您更愿意使用
subprocess.Popen
,那么这里有一个来自该答案的(未测试)代码副本,它使用
subprocess.Popen
OpenProcess
而不是
CreateProcess

import subprocess
import win32api
import win32con
import win32job

hJob = win32job.CreateJobObject(None, "")
extended_info = win32job.QueryInformationJobObject(hJob, win32job.JobObjectExtendedLimitInformation)
extended_info['BasicLimitInformation']['LimitFlags'] = win32job.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
win32job.SetInformationJobObject(hJob, win32job.JobObjectExtendedLimitInformation, extended_info)

child = subprocess.Popen(...)
# Convert process id to process handle:
perms = win32con.PROCESS_TERMINATE | win32con.PROCESS_SET_QUOTA
hProcess = win32api.OpenProcess(perms, False, child.pid)

win32job.AssignProcessToJobObject(hJob, hProcess)
从技术上讲,如果孩子在
Popen
OpenProcess
调用之间死亡,这里有一个很小的竞争条件,你可以决定是否要担心这个问题

使用作业对象的一个缺点是,在Vista或Win7上运行时,如果您的程序是从Windows shell启动的(即通过单击图标),则可能会出现错误,并且尝试创建新作业对象将失败。Win8修复了这一问题(通过允许嵌套作业对象),或者如果您的程序是从命令行运行的,则应该可以

如果您可以修改子项(例如,使用
多处理
),那么最好的选择可能是以某种方式将父项的PID传递给子项(例如,作为命令行参数,或在
args=
参数中传递给
多处理.Process
),然后:

在POSIX上:在子线程中生成一个线程,该线程只是偶尔调用
os.getppid()
,如果返回值停止匹配从父线程传入的pid,则调用
os.\u exit()
。(这种方法可移植到所有Unix,包括OSX,而
prctl
技巧是Linux特有的。)

在Windows上:在子线程中生成一个使用
OpenProcess
os.waitpid
的线程。使用ctypes的示例:

from ctypes import WinDLL, WinError
from ctypes.wintypes import DWORD, BOOL, HANDLE
# Magic value from http://msdn.microsoft.com/en-us/library/ms684880.aspx
SYNCHRONIZE = 0x00100000
kernel32 = WinDLL("kernel32.dll")
kernel32.OpenProcess.argtypes = (DWORD, BOOL, DWORD)
kernel32.OpenProcess.restype = HANDLE
parent_handle = kernel32.OpenProcess(SYNCHRONIZE, False, parent_pid)
# Block until parent exits
os.waitpid(parent_handle, 0)
os._exit(0)
这避免了我提到的作业对象可能出现的任何问题

如果你想非常非常确定,那么你可以结合所有这些解决方案


希望有帮助

这很模糊,你能提供更多的细节吗?也许可以描述一下父进程和子进程是什么?第一个解决方案也可以在Windows上运行。@J.F.Sebastian:当然可以,但是如果进程被
sigkill
终止,第二个解决方案也可以。Windows上没有sigkillWindows@J.F.Sebastian让我换一种说法。如果父进程因任何原因终止,子进程必须退出。第一个解决方案并不能保证这一点,这并不能回答问题。如果父进程崩溃,谁将调用
p.terminate()
?我正在寻找一种方法,在Windows上,启动一个进程,以便在其父进程因任何原因终止时退出。这在Linux上是可以做到的。你是对的,它没有解决你的问题。希望上面的编辑能做到这一点。而且看起来很轻便。我喜欢。这个。。。就我所知,即使在编辑之后,也根本不起作用。看起来你是在建议父母应该观察自己,看看孩子是否死了,如果死了,就杀了孩子。显然,这毫无意义。也许你的意思是,示例代码应该被拆分成一个新程序,这样父进程产生两个进程:子进程和一个监视父进程和子进程的看门狗。但这只是重现了最初的问题——如果看门狗异常终止会发生什么?谁在看《守望者》?啊,我发现我错过了示例上的滚动条,它整齐地剪掉了
if\uuuu name\uuuu==“\uuuu main\uuuu”:
块。这样做更有意义!尽管如此,与更多使用操作系统级工具(Linux和Windows都可以使用)的解决方案相比,这种方法似乎不必要地复杂和不可靠,并为问题带来了新的机会——例如,当前的代码