呵,我昨天自己还在研究这个问题呢!假设你不能改变孩子的程序。
在Linux上,prctl(PR_SET_PDEATHSIG, ...)
可能是唯一可靠的选择。(如果绝对需要杀死子进程,那么你可能想把死亡信号设置为SIGKILL而不是SIGTERM;你链接的代码使用SIGTERM,但如果子进程愿意,它可以选择忽略SIGTERM)。
在Windows上,最可靠的选择是使用一个Job object.这个想法是,你创建一个 "作业"(一种进程的容器),然后你把子进程放入作业中,并设置一个神奇的选项,说 "当没有人持有这个作业的'句柄'时,就杀死其中的进程"。默认情况下,作业的唯一 "句柄 "是你的父进程所持有的句柄,当父进程死亡时,操作系统将浏览并关闭其所有句柄,然后注意到这意味着该作业没有开放的句柄了。所以它就会按照要求杀死这个子进程。(如果你有多个子进程,你可以把它们都分配给同一个作业。)这个答案有示例代码来做这个,使用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外壳启动的(即通过点击一个图标),那么可能会出现已经是一个分配的工作对象试图创建一个新的工作对象将会失败。Win8解决了这个问题(通过允许作业对象嵌套),或者如果你的程序是从命令行运行,那么它应该是好的。
If you 可以修改子代(例如,像使用multiprocessing
时),那么最好的选择可能是以某种方式将父代的PID传递给子代(例如,作为一个命令行参数,或在args=
的参数中传递给multiprocessing.Process
),然后。
在POSIX上:在子程序中产生一个线程,它只是偶尔调用os.getppid()
,如果返回值不再匹配从父程序传入的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)
这就避免了我提到的工作对象的任何可能的问题。
If you want to be really, really sure, then you 可以 combine all these solutions.
希望这对您有所帮助!