podproces: usuwanie procesów potomnych w systemie Windows

W systemie Windows, subprocess.Popen.terminate wywołuje win32 TerminalProcess. Jednak zachowanie, które widzę, jest takie, że procesy potomne procesu, który próbuję zakończyć, nadal działają. Dlaczego? Jak zapewnić, że wszystkie procesy potomne rozpoczęte przez ten proces zostaną zabite?

Author: Piotr Dobrogost, 2009-08-05

6 answers

Używają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)
 50
Author: Giampaolo Rodolà,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-27 01:30:15

Użyj taskkill z flagą /T

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

Flagi do taskkill mają 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.

Lub przejdź przez drzewo procesów za pomocą comtyp 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)
 29
Author: Macke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-26 06:49:35

To jest trudna rzecz do zrobienia. System Windows nie przechowuje drzewa procesów w przestrzeni procesów. Nie jest również możliwe zakończenie procesu i określenie, że jego dzieci również powinny umrzeć.

Jednym z sposobów jest użycie taskkill i powiedzenie mu, aby walnął całe drzewo.

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

Aby zrobić to ogólnie dla siebie, musisz poświęcić trochę czasu na budowanie listy od tyłu. Oznacza to, że proces przechowuje wskaźniki do jego rodzica, ale wydaje się, że rodzice nie przechowują informacji o dzieciach.

Więc musisz spojrzeć na wszystkie procesy w systemie (co naprawdę nie jest takie trudne), a następnie ręcznie połączyć kropki samodzielnie, patrząc na pole procesu nadrzędnego. Następnie wybierz drzewo jesteś zainteresowany i chodzić całość, zabijając każdy węzeł po kolei, jeden po drugim.

Zauważ, że system Windows nie aktualizuje wskaźnika rodzica dziecka, gdy rodzic umiera, więc mogą być luki w drzewie. Nie wiem, co możesz z tym zrobić.

 8
Author: Christopher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-05 02:12:55

Umieść dzieci w obiekcie zadań NT , wtedy możesz zabić wszystkie dzieci

 6
Author: Anders,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-05 04:30:00

Oto przykładowy kod dla metody Job object, 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)
 6
Author: Kevin Smyth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-17 14:40:38

Miałem ten sam problem i po prostu zabijanie procesu za pomocą polecenia windows z opcją zabijania dziecka" / 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)
 3
Author: aka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-20 08:48:15