Як перевірити, чи існує процес із заданим pid у Python?


109

Чи є спосіб перевірити, чи відповідає pid дійсному процесу? Я отримую Pid від іншого джерела, ніж від, os.getpid()і мені потрібно перевірити, чи немає процесу з цим Pid на машині.

Мені потрібно, щоб він був доступний в Unix та Windows. Я також перевіряю, чи PID НЕ використовується.


2
Windows - це нестандартна ОС. Такі речі НЕ переносяться. Знаючи, що ви не можете мати обох, що є вашим пріоритетом? Виберіть пріоритет і відредагуйте питання.
С.Лотт

26
@ S.Lott Windows - це нестандартна ОС Це одне з найдурніших зауважень, які я бачив на SO ...
Piotr Dobrogost

2
@Piotr Dobrogost: Чи можете ви надати код, який обробляє стандарт POSIX та Unix, що не є POSIX? Якщо так, будь ласка, надайте відповідь, що (a) вирішує проблему, і (b) дає зрозуміти, що Windows якимось чином відповідає стандарту POSIX.
S.Lott

3
@PiotrDobrogost Я думаю, що зауваження С.Лотта було більше про деталі впровадження та підтримку API, ніж частку ринку.
Рой Тінкер

3
Windows, безумовно, має менше спільного з іншими популярними ОС, ніж інші. (Кожен, хто займається веб-розробкою, може порівнювати його з аналогічно сумно відомим продуктом Microsoft.) Але у відповідь на @ S.Lott: Я рідко пишу Python-код для Windows, який не повинен також працювати на Linux, OSX, BSD тощо, тому я чесно кажучи, не вважайте, що "вибір як пріоритет" є корисною порадою, тим більше, що Python відводить відмінності платформ настільки, наскільки це можливо.
Майкл Шепер

Відповіді:


162

Надсилання сигналу 0 на pid призведе до виключення OSError, якщо pid не запущено, і нічого іншого не робити.

import os

def check_pid(pid):        
    """ Check For the existence of a unix pid. """
    try:
        os.kill(pid, 0)
    except OSError:
        return False
    else:
        return True

3
Працює точно в Linux та OSX, я не можу говорити для Windows. Він не вбиває процес у тому сенсі, про який ви запитуєте, він посилає сигнал 0, який в основному є "Ви біжите?".
mluebke

10
Це точно не працює в Windows, оскільки немає такого поняття, як UNIX-подібні сигнали.
Олександр Лебедєв

13
Для завершення слід також перевірити наявність помилки, щоб переконатися, що вона дорівнює 3 (зловити виняток і перевірити перший аргумент). Це важливо, якщо цільовий процес існує, але ви не маєте дозволу на надсилання сигналу (з будь-якої причини).
haridsv

8
Зараз підтримується Windows. docs.python.org/library/os.html?highlight=os.kill#os.kill
michael

15
os.kill підтримується у Windows, але os.kill(pid, 0)він такий самий, os.kill(pid, signal.CTRL_C_EVENT)що може припинити процес (або вийти з ладу). Я отримую OSErrorде, errno==EINVALколи я спробую це на підпроцесі.
Джейсон Р. Кумбс

76

Подивіться на psutilмодуль:

psutil (система python та утиліти процесів) - це міжплатформна бібліотека для отримання інформації про запущені процеси та використання системи (процесор, пам'ять, диски, мережа) в Python. [...] В даний час він підтримує Linux , Windows , OSX , FreeBSD і Sun Solaris , як 32-розрядні, так і 64-бітні архітектури, з версіями Python від 2,6 до 3,4 (користувачі Python 2.4 і 2.5 можуть використовувати версію 2.1.3) . Також відомо, що PyPy працює.

Він має функцію, pid_exists()яку ви можете використовувати, щоб перевірити, чи існує процес із заданим під.

Ось приклад:

import psutil
pid = 12345
if psutil.pid_exists(pid):
    print("a process with pid %d exists" % pid)
else:
    print("a process with pid %d does not exist" % pid)

Довідково:



У мене був приклад, коли pid_exists перестав бути надійним у Windows. Це було неправильне повідомлення про мертві пиди як про використовувані. Це нагадування, щоб час від часу оновлювати модуль psutil - особливо під час оновлення ОС.
Деймон Броді

62

код mluebke не на 100% правильний; kill () також може підвищити EPERM (доступ заборонений), у цьому випадку це очевидно означає, що процес існує. Це має працювати:

(Відредаговано згідно з коментарями Джейсона Р. Кумбса)

import errno
import os

def pid_exists(pid):
    """Check whether pid exists in the current process table.
    UNIX only.
    """
    if pid < 0:
        return False
    if pid == 0:
        # According to "man 2 kill" PID 0 refers to every process
        # in the process group of the calling process.
        # On certain systems 0 is a valid PID but we have no way
        # to know that in a portable fashion.
        raise ValueError('invalid PID 0')
    try:
        os.kill(pid, 0)
    except OSError as err:
        if err.errno == errno.ESRCH:
            # ESRCH == No such process
            return False
        elif err.errno == errno.EPERM:
            # EPERM clearly means there's a process to deny access to
            return True
        else:
            # According to "man 2 kill" possible error values are
            # (EINVAL, EPERM, ESRCH)
            raise
    else:
        return True

Це не можна робити в Windows, якщо ви не використовуєте pywin32, ctypes або модуль розширення C. Якщо ви все гаразд, залежно від зовнішньої вкладки, ви можете використовувати psutil :

>>> import psutil
>>> psutil.pid_exists(2353)
True

haridsv припускає, що тест повинен бути e.errno! = 3; можливо e.errno! = errno.ESRCH
Джейсон Р. Кумбс

Чому? ESRCH означає "такого процесу немає".
Giampaolo Rodolà

2
Правильно. Оскільки ESRCH означає "немає такого процесу", errno! = ESRCH означає "немає такого процесу" або "процес існує", що дуже схоже на назву функції. Ви конкретно згадали про EPERM, але що означають інші можливі коди помилок? Здається, неправильно виділяти код помилки, який слабко пов'язаний з наміром перевірки, тоді як ESRCH здається тісно пов'язаним.
Джейсон Р. Кумбс

Ти маєш рацію. Я відредагував код, який тепер відображає те, що ви запропонували.
Джампаоло Родола

in python3 os.kill () кидає ProcessLookupError
martinkunev

19

Відповіді, що стосуються надсилання «сигналу 0» до процесу, працюватимуть лише в тому випадку, якщо процес, про який йде мова, належить користувачеві, який виконує тест . Інакше ви отримаєте OSErrorчерез дозволи , навіть якщо pid існує в системі.

Щоб обійти це обмеження, ви можете перевірити, чи /proc/<pid>існує:

import os

def is_running(pid):
    if os.path.isdir('/proc/{}'.format(pid)):
        return True
    return False

Очевидно, це стосується лише систем на базі Linux.


неправильно. PermissionErrorозначає, що pid існує , ви отримуєте, ProcessLookupErrorякщо pid не існує.
jfs

OSErrorЗ - за відмови в дозволі можна відрізнити від інших - або через дивлячись на егто або через затримання більш спеціалізовані PermissionError/ ProcessLookupErrorвиключення , які випливають з OSError. Крім того, ви отримуєте помилку дозволу лише тоді, коли процес існує. Таким чином, ваш приклад - це лише альтернативний метод, який працює в Linux та деяких інших Unices, але він не є більш повним, ніж правильно викликати os.kill(pid, 0).
maxschlepzig

Це рішення не є корсовою платформою. ОП хочуть, щоб він був доступний на Unix та Windows. У /procPROCFS виходить тільки на Linux, навіть не на BSD або OSX.
Чарльз

Цей спосіб виходить з ладу, якщо / proc встановлено скриптом = 2, процес не відображатиметься у списку, якщо він належить іншому користувачеві.
Перкінс

8

У Python 3.3+ ви можете використовувати назви виключень замість констант errno. Версія Posix :

import os

def pid_exists(pid): 
    if pid < 0: return False #NOTE: pid == 0 returns True
    try:
        os.kill(pid, 0) 
    except ProcessLookupError: # errno.ESRCH
        return False # No such process
    except PermissionError: # errno.EPERM
        return True # Operation not permitted (i.e., process exists)
    else:
        return True # no error, we can send a signal to the process

7

Тут знайдіть специфічний для Windows спосіб отримання повного списку запущених процесів з їх ідентифікаторами. Це було б щось на кшталт

from win32com.client import GetObject
def get_proclist():
    WMI = GetObject('winmgmts:')
    processes = WMI.InstancesOf('Win32_Process')
    return [process.Properties_('ProcessID').Value for process in processes]

Потім ви можете перевірити отриманий вами під у цьому списку. Я не маю уявлення про вартість продуктивності, тому краще перевірити це, якщо ви збираєтеся часто перевіряти pid.

Для * NIx просто використовуйте розчин mluebke.


Це добре спрацювало для мене. Я хотів перевірити, чи є ім'я процесу, так що він замінив "ProcessID" на "Name", а також перетворив його на чек у списку, щоб повернути True чи False.
JamesR

6

Опираючись на ntrrgc, я збільшив версію Windows, щоб вона перевіряла код виходу з процесу та перевіряла дозволи:

def pid_exists(pid):
    """Check whether pid exists in the current process table."""
    if os.name == 'posix':
        import errno
        if pid < 0:
            return False
        try:
            os.kill(pid, 0)
        except OSError as e:
            return e.errno == errno.EPERM
        else:
            return True
    else:
        import ctypes
        kernel32 = ctypes.windll.kernel32
        HANDLE = ctypes.c_void_p
        DWORD = ctypes.c_ulong
        LPDWORD = ctypes.POINTER(DWORD)
        class ExitCodeProcess(ctypes.Structure):
            _fields_ = [ ('hProcess', HANDLE),
                ('lpExitCode', LPDWORD)]

        SYNCHRONIZE = 0x100000
        process = kernel32.OpenProcess(SYNCHRONIZE, 0, pid)
        if not process:
            return False

        ec = ExitCodeProcess()
        out = kernel32.GetExitCodeProcess(process, ctypes.byref(ec))
        if not out:
            err = kernel32.GetLastError()
            if kernel32.GetLastError() == 5:
                # Access is denied.
                logging.warning("Access is denied to get pid info.")
            kernel32.CloseHandle(process)
            return False
        elif bool(ec.lpExitCode):
            # print ec.lpExitCode.contents
            # There is an exist code, it quit
            kernel32.CloseHandle(process)
            return False
        # No exit code, it's running.
        kernel32.CloseHandle(process)
        return True

Насправді, згідно з повідомленнями msdn.microsoft.com/en-us/library/windows/desktop/… , GetExistCodeProcessпотрібні права доступу PROCESS_QUERY_INFORMATIONта PROCESS_QUERY_LIMITED_INFORMATIONдоступу.
серпень

Зауважте, що код неправильний. GetExitCodeProcessотримує ручку та покажчик, і в цьому прикладі він отримує ExitCodeProcessструктуру як другий параметр, коли він повинен бути лише вказівником.
Фабіо Задрозний

Після OpenProcess непогано перевірити GetLastError. ERROR_ACCESS_DENIED там означає, що процес існує! Ось повний приклад використання цього: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule

4

Поєднуючи відповідь Джампаоло Родола для POSIX та мою для Windows, я зрозумів:

import os
if os.name == 'posix':
    def pid_exists(pid):
        """Check whether pid exists in the current process table."""
        import errno
        if pid < 0:
            return False
        try:
            os.kill(pid, 0)
        except OSError as e:
            return e.errno == errno.EPERM
        else:
            return True
else:
    def pid_exists(pid):
        import ctypes
        kernel32 = ctypes.windll.kernel32
        SYNCHRONIZE = 0x100000

        process = kernel32.OpenProcess(SYNCHRONIZE, 0, pid)
        if process != 0:
            kernel32.CloseHandle(process)
            return True
        else:
            return False

Версія Windows не працює для мене на Windows 8.1. Ви повинні перевірити GetExitCodeProcessі переконатися, що у вас навіть доступ.
швидкісний літак

Використовувати kernel32.OpenProcessвиключно недостатньо. Як зазначалося тут , "якщо процес завершився недавно, під все ще може існувати для ручки". Якщо kernel32.OpenProcessповертає ненульове значення, нам ще потрібно kernel32.GetExitCodeProcessперевірити код виходу.
Мяу

2

У Windows ви можете це зробити таким чином:

import ctypes
PROCESS_QUERY_INFROMATION = 0x1000
def checkPid(pid):
    processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFROMATION, 0,pid)
    if processHandle == 0:
        return False
    else:
        ctypes.windll.kernel32.CloseHandle(processHandle)
    return True

Перш за все, у цьому коді ви намагаєтеся отримати обробку для процесу з наданим pid. Якщо ручка дійсна, закрийте ручку для процесу та поверніть True; в іншому випадку ви повернете Неправдиво. Документація для OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx


2

Це буде працювати для Linux, наприклад, якщо ви хочете перевірити, чи працює banshee ... (banshee - це музичний плеєр)

import subprocess

def running_process(process):
    "check if process is running. < process > is the name of the process."

    proc = subprocess.Popen(["if pgrep " + process + " >/dev/null 2>&1; then echo 'True'; else echo 'False'; fi"], stdout=subprocess.PIPE, shell=True)

    (Process_Existance, err) = proc.communicate()
    return Process_Existance

# use the function
print running_process("banshee")

Цей метод явно поступається порівняно з використанням os.kill(pid, 0)або переглядом /proc/{pid}. Замість того, щоб виконати один syscall, ваш код роздвоює дитину, виконує оболонку в цій дитині, оболонка інтерпретує ваш зайвий міні-скрипт оболонки, оболонка виличить іншу дитину, яка виконує pgrep і, нарешті, pgrep ітератує /proc. Ваша відповідь не відповідає на розміщене запитання. ОП попросив метод, який дав PID. Для вашого методу потрібна назва процесу.
maxschlepzig

-2

Я б сказав, використовуйте PID для будь-якої мети, яку ви отримуєте, і обробляйте помилки витончено. В іншому випадку це класична гонка (PID може бути дійсним, коли ви перевірите, чи справді це, але підете мить пізніше)


Я повинен був бути більш конкретним - я перевіряю НЕЗАКОНІСТЬ. Отже, я в основному хочу бачити, чи не використовується ПІД.
Еван Фосмарк

1
Але що ви зробите з цією відповіддю? Миттєво після того, як ви отримаєте ці знання, щось може скористатись цим приводом.
Damien_The_Unbeliever

@Damien_The_Unbeliever - це добре, якщо щось використовує після того, як я отримаю ці знання, і я зрозумію, що ти кажеш про стан гонки, але можу запевнити, що це не стосується моєї ситуації.
Еван Фосмарк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.