子:在Windows中删除子进程
-
22-07-2019 - |
题
在Windows中,subprocess.Popen.terminate
调用的Win32的TerminalProcess
。但是,我看到的行为是进程的子进程我试图终止仍在运行。这是为什么?如何确保进程启动后所有子进程被杀死?
解决方案
通过使用 psutil :
import psutil, os
def kill_proc_tree(pid, including_parent=True):
parent = psutil.Process(pid)
children = parent.children(recursive=True)
for child in children:
child.kill()
gone, still_alive = psutil.wait_procs(children, timeout=5)
if including_parent:
parent.kill()
parent.wait(5)
me = os.getpid()
kill_proc_tree(me)
其他提示
使用taskkill
与/T
标志
p = subprocess.Popen(...)
<wait>
subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)])
要TASKKILL这些标志具有以下文档:
TASKKILL [/S system [/U username [/P [password]]]]
{ [/FI filter] [/PID processid | /IM imagename] } [/T] [/F]
/S system Specifies the remote system to connect to.
/U [domain\]user Specifies the user context under which the
command should execute.
/P [password] Specifies the password for the given user
context. Prompts for input if omitted.
/FI filter Applies a filter to select a set of tasks.
Allows "*" to be used. ex. imagename eq acme*
/PID processid Specifies the PID of the process to be terminated.
Use TaskList to get the PID.
/IM imagename Specifies the image name of the process
to be terminated. Wildcard '*' can be used
to specify all tasks or image names.
/T Terminates the specified process and any
child processes which were started by it.
/F Specifies to forcefully terminate the process(es).
/? Displays this help message.
或者使用comtypes和WIN32API行走过程树:
def killsubprocesses(parent_pid):
'''kill parent and all subprocess using COM/WMI and the win32api'''
log = logging.getLogger('killprocesses')
try:
import comtypes.client
except ImportError:
log.debug("comtypes not present, not killing subprocesses")
return
logging.getLogger('comtypes').setLevel(logging.INFO)
log.debug('Querying process tree...')
# get pid and subprocess pids for all alive processes
WMI = comtypes.client.CoGetObject('winmgmts:')
processes = WMI.InstancesOf('Win32_Process')
subprocess_pids = {} # parent pid -> list of child pids
for process in processes:
pid = process.Properties_('ProcessID').Value
parent = process.Properties_('ParentProcessId').Value
log.trace("process %i's parent is: %s" % (pid, parent))
subprocess_pids.setdefault(parent, []).append(pid)
subprocess_pids.setdefault(pid, [])
# find which we need to kill
log.debug('Determining subprocesses for pid %i...' % parent_pid)
processes_to_kill = []
parent_processes = [parent_pid]
while parent_processes:
current_pid = parent_processes.pop()
subps = subprocess_pids[current_pid]
log.debug("process %i children are: %s" % (current_pid, subps))
parent_processes.extend(subps)
processes_to_kill.extend(subps)
# kill the subprocess tree
if processes_to_kill:
log.info('Process pid %i spawned %i subprocesses, terminating them...' %
(parent_pid, len(processes_to_kill)))
else:
log.debug('Process pid %i had no subprocesses.' % parent_pid)
import ctypes
kernel32 = ctypes.windll.kernel32
for pid in processes_to_kill:
hProcess = kernel32.OpenProcess(PROCESS_TERMINATE, FALSE, pid)
if not hProcess:
log.warning('Unable to open process pid %i for termination' % pid)
else:
log.debug('Terminating pid %i' % pid)
kernel32.TerminateProcess(hProcess, 3)
kernel32.CloseHandle(hProcess)
这是一个很难的事情。 Windows不实际存储的过程树的进程空间。也不可能终止的方法和指定它的孩子也应该死。
周围的一种方法是使用的taskkill并告诉它怪人整个树。
另一种方式来做到这一点(假设你正在产卵的顶级工艺)是使用与这样的事情而开发的模块:的 http://benjamin.smedbergs.us/blog/tag/killableprocess/
为了做到这一点一般的自己,你必须花一些时间向后建筑名单。也就是说,一个过程存储指向它的母公司,但家长似乎并不存储有关孩子的信息。
所以,你必须看看系统中的所有进程(这真的不是那么难),然后手动通过查看父进程场自己连接点。然后,您选择的树,你有兴趣,走了整个事情,一个杀死依次在每个节点之一。
请注意,当父母去世的Windows不更新孩子的父亲指针,所以有可能是树中的空白。我不知道的事情,你可以做这些。
以下是适用于作业对象方法的示例代码,但不是subprocess
它使用win32api.CreateProcess
import win32process
import win32job
startup = win32process.STARTUPINFO()
(hProcess, hThread, processId, threadId) = win32process.CreateProcess(None, command, None, None, True, win32process.CREATE_BREAKAWAY_FROM_JOB, None, None, startup)
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)
win32job.AssignProcessToJobObject(hJob, hProcess)
把学生在NT 作业对象一>,那么你可以杀死所有的孩子
我有同样的问题,只是通过Windows查杀过程与选项子杀人“/ T”命令
def kill_command_windows(pid):
'''Run command via subprocess'''
dev_null = open(os.devnull, 'w')
command = ['TASKKILL', '/F', '/T', '/PID', str(pid)]
proc = subprocess.Popen(command, stdin=dev_null, stdout=sys.stdout, stderr=sys.stderr)
我用凯文-史密斯的答案创建直接替代以subprocess.Popen
该界限在一个匿名作业对象所创建的子进程,设置为靠近终止于:
# coding: utf-8
from subprocess import Popen
import subprocess
import win32job
import win32process
import win32api
class JobPopen(Popen):
"""Start a process in a new Win32 job object.
This `subprocess.Popen` subclass takes the same arguments as Popen and
behaves the same way. In addition to that, created processes will be
assigned to a new anonymous Win32 job object on startup, which will
guarantee that the processes will be terminated by the OS as soon as
either the Popen object, job object handle or parent Python process are
closed.
"""
class _winapijobhandler(object):
"""Patches the native CreateProcess function in the subprocess module
to assign created threads to the given job"""
def __init__(self, oldapi, job):
self._oldapi = oldapi
self._job = job
def __getattr__(self, key):
if key != "CreateProcess":
return getattr(self._oldapi, key) # Any other function is run as before
else:
return self.CreateProcess # CreateProcess will call the function below
def CreateProcess(self, *args, **kwargs):
hp, ht, pid, tid = self._oldapi.CreateProcess(*args, **kwargs)
win32job.AssignProcessToJobObject(self._job, hp)
win32process.ResumeThread(ht)
return hp, ht, pid, tid
def __init__(self, *args, **kwargs):
"""Start a new process using an anonymous job object. Takes the same arguments as Popen"""
# Create a new job object
self._win32_job = self._create_job_object()
# Temporarily patch the subprocess creation logic to assign created
# processes to the new job, then resume execution normally.
CREATE_SUSPENDED = 0x00000004
kwargs.setdefault("creationflags", 0)
kwargs["creationflags"] |= CREATE_SUSPENDED
try:
_winapi = subprocess._winapi # Python 3
_winapi_key = "_winapi"
except AttributeError:
_winapi = subprocess._subprocess # Python 2
_winapi_key = "_subprocess"
try:
setattr(subprocess, _winapi_key, JobPopen._winapijobhandler(_winapi, self._win32_job))
super(JobPopen, self).__init__(*args, **kwargs)
finally:
setattr(subprocess, _winapi_key, _winapi)
def _create_job_object(self):
"""Create a new anonymous job object"""
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)
return hjob
def _close_job_object(self, hjob):
"""Close the handle to a job object, terminating all processes inside it"""
if self._win32_job:
win32api.CloseHandle(self._win32_job)
self._win32_job = None
# This ensures that no remaining subprocesses are found when the process
# exits from a `with JobPopen(...)` block.
def __exit__(self, exc_type, value, traceback):
super(JobPopen, self).__exit__(exc_type, value, traceback)
self._close_job_object(self._win32_job)
# Python does not keep a reference outside of the parent class when the
# interpreter exits, which is why we keep it here.
_Popen = subprocess.Popen
def __del__(self):
self._Popen.__del__(self)
self._close_job_object(self._win32_job)