python - subproceso: eliminar procesos secundarios en Windows
process subprocess (6)
Al usar 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)
En Windows, subprocess.Popen.terminate
llama a TerminalProcess
de win32. Sin embargo, el comportamiento que veo es que los procesos hijos del proceso que estoy tratando de finalizar aún se están ejecutando. ¿Porqué es eso? ¿Cómo me aseguro de que se maten todos los procesos secundarios iniciados por el proceso?
Aquí hay un código de ejemplo para el método de objeto Job, pero en lugar de un subprocess
, usa 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)
Esto es algo difícil de hacer. Windows no almacena un árbol de procesos en el espacio de proceso. Tampoco es posible finalizar un proceso y especificar que sus hijos también deben morir.
Una forma de evitarlo es usar taskkill y decirle a wack todo el árbol.
Otra forma de hacerlo (suponiendo que está generando el proceso de nivel superior) es utilizar un módulo que se desarrolló con este tipo de cosas en mente: http://benjamin.smedbergs.us/blog/tag/killableprocess/
Para hacer esto genéricamente para ti, tienes que dedicar un tiempo a construir la lista al revés. Es decir, un proceso almacena punteros a su PADRE, pero los padres parecen no almacenar información sobre los niños.
Por lo tanto, debe observar todos los procesos del sistema (que en realidad no es tan difícil) y luego, manualmente, conectar los puntos usted mismo mirando el campo de proceso principal. Luego, selecciona el árbol en el que estás interesado y lo recorre todo, matando a cada nodo uno por uno.
Tenga en cuenta que Windows no actualiza el puntero principal de un hijo cuando el padre muere, por lo que puede haber lagunas en su árbol. No estoy al tanto de nada que puedas hacer al respecto.
Tuve el mismo problema y simplemente el proceso de matar a través del comando de Windows con la opción de matar niño "/ 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)
Usa taskkill
con la bandera /T
p = subprocess.Popen(...)
<wait>
subprocess.call([''taskkill'', ''/F'', ''/T'', ''/PID'', str(p.pid)])
Los flags to taskkill tienen los siguientes documentos:
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.
O recorra el árbol de procesos usando comtypes y 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)