subprocess: deleting child processes in Windows
By using psutil:
import psutil, osdef 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)
Use taskkill
with the /T
flag
p = subprocess.Popen(...)<wait>subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)])
The flags to taskkill has the following docs:
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.
Or walk the process tree using comtypes and 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)
Here's example code for the Job object method, but instead of subprocess
it uses win32api.CreateProcess
import win32processimport win32jobstartup = 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_CLOSEwin32job.SetInformationJobObject(hJob, win32job.JobObjectExtendedLimitInformation, extended_info)win32job.AssignProcessToJobObject(hJob, hProcess)