Python:当父母死亡时如何杀死subprocess?
subprocess开始于
subprocess.Popen(arg)
有没有办法确保父母exception终止时会死亡? 我需要这个在Windows和Linux上工作。 我知道这个Linux的解决scheme 。
编辑:
使用subprocess启动subprocess的需求。如果解决scheme使用不同的启动进程的方法存在,则可以放宽subprocess.Popen(arg)
。
嘿,我昨天刚刚自己研究这个 假设你不能改变子程序:
在Linux上, prctl(PR_SET_PDEATHSIG, ...)
可能是唯一可靠的select。 (如果subprocess绝对有必要被杀死,那么你可能需要将死亡信号设置为SIGKILL而不是SIGTERM;链接的代码使用SIGTERM,但是如果孩子愿意的话,可以select忽略SIGTERM。 )
在Windows上,最可靠的select是使用Job对象 。 这个想法是,你创build一个“作业”(一种进程的容器),然后你把subprocess放到作业中,然后设置魔术选项“当没有人持有这个作业的”句柄“ ,然后杀死其中的进程“。 默认情况下,作业的唯一“句柄”就是父进程持有的句柄,当父进程死亡时,操作系统将会通过并closures其所有句柄,然后注意到这意味着没有打开的句柄工作。 那么它按照要求杀死了孩子。 (如果您有多个subprocess,则可以将它们全部分配给相同的作业。) 此答案使用win32api
模块进行此操作的示例代码。 该代码使用CreateProcess
启动子代,而不是subprocess.Popen
。 原因是他们需要得到产生的subprocess的“进程句柄”,而CreateProcess
默认返回这个进程句柄。 如果你想使用subprocess.Popen
,那么这个答案的代码副本(未经testing)使用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启动的(例如,通过点击一个图标),那么可能已经有一个工作对象分配,并试图创build一个新的工作对象将失败。 Win8修复了这个问题(通过允许作业对象被嵌套),或者如果你的程序是从命令行运行,那么它应该没问题。
如果您可以修改孩子(例如,就像使用multiprocessing
),那么最好的select是以某种方式将父母的PID传递给孩子(例如作为命令行参数,或者在multiprocessing.Process
的args=
参数中)接着:
在POSIX上:在子代中产生一个只是偶尔调用os.getppid()
的线程,如果返回值永远停止匹配从父os._exit()
传入的pid,则调用os._exit()
。 (这种方法可以移植到所有的Unix上,包括OS X,而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)
这避免了我提到的任何工作对象的任何可能的问题。
如果你想真的,真的确定,那么你可以结合所有这些解决scheme。
希望有所帮助!
Popen对象提供了终止和终止方法。
https://docs.python.org/2/library/subprocess.html#subprocess.Popen.terminate
这些为您发送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()
更新:
你是正确的 – 上面的代码不会防止硬件崩溃或有人杀死你的过程。 在这种情况下,您可以尝试在一个类中包装subprocess,并使用轮询模型来监视父进程。 请注意,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。 如果杀死父进程,subprocess(ping命令)也将被终止。