subprocess: deleting child processes in Windows
Solution 1
By using 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)
Solution 2
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)
Solution 3
Here's example code for the Job object method, but instead of subprocess
it uses 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)
Solution 4
This is a hard thing to do. Windows does not actually store a process tree in the process space. Nor is it possible to terminate a process and specify that it's children should also die.
One way around that is to use taskkill and tell it to wack the whole tree.
Another way to do it (assuming that you are spawning the top-level process) is to use a module that was developed with this sort of thing in mind: http://benjamin.smedbergs.us/blog/tag/killableprocess/
In order to do this generically for yourself, you have to spend some time building the list backwards. That is, a process stores pointers to it's PARENT, but parents appear to not store information about children.
So you have to look at all the processes in the system (which really isn't that hard), and then manually connect the dots yourself by looking at the parent process field. Then, you select the tree you are interested in and walk the whole thing, killing each node in turn, one by one.
Note that Windows doesn't update a child's parent pointer when the parent dies, so there may be gaps in your tree. I'm not aware of anything you can do about those.
Solution 5
Put the children in a NT Job object, then you can kill all children
Related videos on Youtube
Sridhar Ratnakumar
Updated on September 01, 2021Comments
-
Sridhar Ratnakumar over 2 years
On Windows,
subprocess.Popen.terminate
calls win32'sTerminalProcess
. However, the behavior I see is that child processes of the process I am trying to terminate are still running. Why is that? How do I ensure all child processes started by the process are killed?-
Unknown over 14 yearsHere are 2 options 1. Use this exe as a subprocess which kills process trees for you: latenighthacking.com/projects/2002/kill 2. Convert the following C code into Python with ctypes: stackoverflow.com/questions/1173342/…
-
Piotr Dobrogost over 11 yearsWhich version of Python and Windows?
-
-
Sridhar Ratnakumar over 14 yearsJob objects seems like the right way to approach this problem; too bad that this isn't integrated with the subprocess module.
-
ICTMitchell almost 12 yearsThat code looks like it will only kill first-level children, not grandchildren etc. Might be an issue if you're launching build tools or .bat/.cmd files with cmd.exe. Unless get_children() means grandchildren too?
-
Giampaolo Rodolà over 11 yearsExactly. To include grandchildren you should specify the 'recursive' option as in parent.get_children(recursive=True)
-
Gilead over 11 yearsThis is an old answer, but it was exactly what I was looking for -- a cross-platform way of killing all child processes. All the other answers that showed up on Google claim it cannot be done. Thank you for psutil!
-
jfs almost 10 years+1: it seems like the most robust solution that works even if the parent process crashed. Here's the explanation on how it works
-
Eryk Sun over 8 yearsNote that Windows doesn't maintain a process tree.
parent.children(recursive=True)
is building a tree on the fly by linking parent to child, so it won't find orphaned processes (i.e. if the parent died). -
SaundersB over 7 yearsThank you for this.
-
Ken Pronovici over 6 yearsI was attempting to kill a Gradle build process kicked off with
subprocess.Popen()
. A simpleprocess.terminate()
orprocess.kill()
did not work on WIndows 7, and neither did thepsutils
option above, but this did. -
naitsirhc over 6 yearsThanks very much for this! It's exactly what I was looking for. Would you agree to let the code in this answer to be used under open source licensing terms? BSD or MIT would be ideal, since they are compatible with Numpy, Pandas, Scipy, etc.
-
andreykyz almost 4 yearsIt doesn't work all time. If process can spawn randomly.
-
Elyasaf755 over 2 yearsThis is the only solution that worked for me after trying more about ~15 suggestions. Instantiate a subprocess using the JobPopen like this: with open(JobPopen(command, stdout=subprocess.PIPE, shell=True)) as p: # YOUR CODE HERE # And then, at the moment the script exits the 'with open' block - it will close that process.