2009-08-05 16 views
31

W systemie Windows: subprocess.Popen.terminate dzwoni do win32: TerminalProcess. Jednak zachowanie, które widzę, to to, że proces potomny procesu, który próbuję zakończyć, nadal działa. Dlaczego? Jak mogę zabezpieczyć wszystkie procesy potomne rozpoczęte przez proces?podproces: usuwanie procesów podrzędnych w systemie Windows

+0

Która wersja Pythona i Windowsa? –

+0

Oto 2 opcje 1. Użyj tego exe jako podprocesu, który zabija dla ciebie drzewa procesów: http://www.latenighthacking.com/projects/2002/kill/ 2. Przekształć następujący kod C w Python za pomocą ctypes: http: //stackoverflow.com/questions/1173342/terminate-a-process-tree-c-for-windows – Unknown

Odpowiedz

49

Korzystając 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) 
+1

Ten kod wygląda na to, że zabije tylko dzieci pierwszego poziomu, a nie wnuki itp. Może to być problem, jeśli uruchamiasz narzędzia do budowania lub pliki .bat/.cmd przy pomocy cmd.exe.Chyba, że ​​get_children() oznacza również wnuki? – Macke

+1

Dokładnie. Aby włączyć wnuki, powinieneś określić opcję "rekursywną", tak jak w parent.get_children (rekursywna = prawda). –

+5

To stara odpowiedź, ale dokładnie to, czego szukałem - międzyplatformowy sposób zabijania wszystkich procesów podrzędnych. Wszystkie inne odpowiedzi, które pojawiły się w Google, nie pozwalają na to. Dziękuję za psutil! – Gilead

7

Trudno to zrobić. System Windows faktycznie nie przechowuje drzewa procesów w przestrzeni procesu. Nie można też zakończyć procesu i określić, że dzieci powinny także umrzeć.

Jednym ze sposobów jest użycie taskkill i nakazanie, aby zwinął całe drzewo.

Innym sposobem na to (zakładając, że jesteś tarła proces najwyższego poziomu) jest użycie modułu, który został opracowany z tego typu rzeczy na uwadze: http://benjamin.smedbergs.us/blog/tag/killableprocess/

Aby to zrobić rodzajowo dla siebie , musisz poświęcić trochę czasu na budowanie listy wstecz. Oznacza to, że proces przechowuje wskaźniki do PARENT, ale rodzice wydają się nie przechowywać informacji o dzieciach.

Musisz więc przyjrzeć się wszystkim procesom w systemie (co naprawdę nie jest takie trudne), a następnie samodzielnie połączyć kropki, patrząc na pole procesu nadrzędnego. Następnie wybierasz drzewo, które cię interesuje i wszystko idziesz, zabijając kolejno każdy węzeł, jeden po drugim.

Należy pamiętać, że system Windows nie aktualizuje wskaźnika rodzica dziecka, gdy rodzic umiera, dlatego w drzewie mogą występować przerwy. Nie jestem świadomy niczego, co możesz z tym zrobić.

6

umieścić dzieci w NT Job object, można zabić wszystkie dzieci

+3

Obiekty zadania wydają się być właściwym sposobem podejścia do tego problemu; szkoda, że ​​nie jest to zintegrowane z modułem podprocesu. –

27

Korzystając taskkill z flagą /T

p = subprocess.Popen(...) 
<wait> 
subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)]) 

Flagi do taskkill ma następujące dokumenty:

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. 

Albo chodzić drzewo procesów za pomocą comtypes i 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) 
+0

Dziękuję za to. – Speakeasys

6

Oto przykładowy kod dla metody obiektu zadania, ale zamiast subprocess używa 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) 
+0

+1: wydaje się najbardziej niezawodnym rozwiązaniem, które działa, nawet jeśli proces nadrzędny ulegnie awarii. Oto [wyjaśnienie, jak to działa] (http://stackoverflow.com/a/23587108/4279) – jfs

3

miałem ten sam problem i po prostu zabijanie za pomocą polecenia Windows z opcją zabijania dzieci "/ 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) 
+0

Próbowałem zabić proces kompilacji Gradle rozpoczynam z 'subprocess.Popen()'. Proste 'process.terminate()' lub 'process.kill()' nie działały na Windows 7, podobnie jak opcja 'psutils' powyżej, ale tak właśnie było. –

Powiązane problemy