سؤال

على ويندوز ، subprocess.Popen.terminate استدعاءات win32 هو TerminalProcess.ومع ذلك ، فإن السلوك الذي أراه هو أن الطفل العمليات من عملية أحاول إنهاء تزال تعمل.لماذا هذا ؟ كيف يمكنني التأكد من جميع الطفل العمليات التي كتبها عملية قتل ؟

هل كانت مفيدة؟

المحلول

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)

نصائح أخرى

استخدم taskkill مع العلم /T

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

والأعلام لtaskkill لديه مستندات التالية:

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.

وأو المشي الشجرة العملية باستخدام comtypes و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)

وهذا هو الشيء الصعب القيام به. ويندوز لا فعلا تخزين شجرة عملية في مساحة العملية. ولا هو ممكن لإنهاء عملية وتحديد أنه يجب على الأطفال أيضا يموت.

وطريقة واحدة حول ذلك هو استخدام taskkill وأقول ذلك لالصديق الشجرة كلها.

وهناك طريقة أخرى للقيام بذلك (على افتراض أنك تفريخ عملية على مستوى أعلى) هو استخدام وحدة نمطية التي تم تطويرها مع هذا النوع من الأشياء في الاعتبار: <لأ href = "http://benjamin.smedbergs.us / بلوق / العلامة / killableprocess / "يختلط =" noreferrer "> http://benjamin.smedbergs.us/blog/tag/killableprocess/

في أجل القيام بذلك بشكل عام لنفسك، لديك لقضاء بعض الوقت بناء قائمة الوراء. وهذا هو، عملية مخازن مؤشرات إلى أنها الأم، ولكن الآباء ويبدو أن لا تخزين المعلومات عن الأطفال.

وهكذا لا بد من النظر في جميع العمليات في النظام (الذي هو في الحقيقة ليس من الصعب)، وبعد ذلك يدويا الربط بين النقاط نفسك من خلال النظر في مجال عملية الأصل. ثم حدد الشجرة كنت مهتما في والسير في كل شيء، مما أسفر عن مقتل كل عقدة في المقابل، واحدا تلو الآخر.

لاحظ أن Windows لا يتم تحديث مؤشر ولي أمر الطفل عند وفاة الوالد، لذلك قد يكون هناك ثغرات في شجرة الخاص بك. أنا لست على علم بأي شيء يمكنك القيام به حيال تلك.

وهنا رمز المثال للأسلوب كائن العمل، ولكن بدلا من subprocess أنه يستخدم 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)

وضع الأطفال في NT وظيفة الكائن, ثم أنت يمكن أن تقتل جميع الأطفال

وكان لي نفس المشكلة ومجرد عملية قتل عبر النوافذ قيادة مع خيار لقتل الأطفال "/ 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)

واعتدت الجواب كيفن سميث لإنشاء rel="nofollow غلبه في استبدال للحصول على subprocess.Popen أن حدود عملية الطفل التي تم إنشاؤها في كائن وظيفة مجهول، التي أنشئت من أجل إنهاء على وثيقة:

# coding: utf-8

from subprocess import Popen
import subprocess
import win32job
import win32process
import win32api


class JobPopen(Popen):
    """Start a process in a new Win32 job object.

    This `subprocess.Popen` subclass takes the same arguments as Popen and
    behaves the same way. In addition to that, created processes will be
    assigned to a new anonymous Win32 job object on startup, which will
    guarantee that the processes will be terminated by the OS as soon as
    either the Popen object, job object handle or parent Python process are
    closed.
    """

    class _winapijobhandler(object):
        """Patches the native CreateProcess function in the subprocess module
        to assign created threads to the given job"""

        def __init__(self, oldapi, job):
            self._oldapi = oldapi
            self._job = job

        def __getattr__(self, key):
            if key != "CreateProcess":
                return getattr(self._oldapi, key)  # Any other function is run as before
            else:
                return self.CreateProcess  # CreateProcess will call the function below

        def CreateProcess(self, *args, **kwargs):
            hp, ht, pid, tid = self._oldapi.CreateProcess(*args, **kwargs)
            win32job.AssignProcessToJobObject(self._job, hp)
            win32process.ResumeThread(ht)
            return hp, ht, pid, tid

    def __init__(self, *args, **kwargs):
        """Start a new process using an anonymous job object. Takes the same arguments as Popen"""

        # Create a new job object
        self._win32_job = self._create_job_object()

        # Temporarily patch the subprocess creation logic to assign created
        # processes to the new job, then resume execution normally.
        CREATE_SUSPENDED = 0x00000004
        kwargs.setdefault("creationflags", 0)
        kwargs["creationflags"] |= CREATE_SUSPENDED
        try:
            _winapi = subprocess._winapi  # Python 3
            _winapi_key = "_winapi"
        except AttributeError:
            _winapi = subprocess._subprocess  # Python 2
            _winapi_key = "_subprocess"
        try:
            setattr(subprocess, _winapi_key, JobPopen._winapijobhandler(_winapi, self._win32_job))
            super(JobPopen, self).__init__(*args, **kwargs)
        finally:
            setattr(subprocess, _winapi_key, _winapi)

    def _create_job_object(self):
        """Create a new anonymous job object"""
        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)
        return hjob

    def _close_job_object(self, hjob):
        """Close the handle to a job object, terminating all processes inside it"""
        if self._win32_job:
            win32api.CloseHandle(self._win32_job)
            self._win32_job = None

    # This ensures that no remaining subprocesses are found when the process
    # exits from a `with JobPopen(...)` block.
    def __exit__(self, exc_type, value, traceback):
        super(JobPopen, self).__exit__(exc_type, value, traceback)
        self._close_job_object(self._win32_job)

    # Python does not keep a reference outside of the parent class when the
    # interpreter exits, which is why we keep it here.
    _Popen = subprocess.Popen  
    def __del__(self):
        self._Popen.__del__(self)
        self._close_job_object(self._win32_job)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top