Как проверить, существует ли в Python процесс с заданным pid?


109

Есть ли способ проверить, соответствует ли pid действующему процессу? Я получаю pid из другого источника, кроме from, os.getpid()и мне нужно проверить, не существует ли на машине процесса с этим pid.

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


2
Windows - нестандартная ОС. Такие вещи НЕ переносимы. Зная, что у вас не может быть того и другого, что является вашим приоритетом? Выберите один в качестве приоритетного и отредактируйте вопрос.
S.Lott

26
@ S.Lott Windows - нестандартная ОС. Это одно из самых глупых замечаний, которые я видел на SO ...
Петр Доброгост,

2
@Piotr Dobrogost: Можете ли вы предоставить код, который обрабатывает стандартный unix POSIX и не-POSIX стандарт Windows? Если да, дайте ответ, который (а) решает проблему и (б) дает понять, что Windows каким-то образом соответствует стандарту POSIX.
S.Lott

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

3
Windows, безусловно, имеет меньше общего с другими популярными ОС, чем остальные друг с другом. (Любой, кто занимается веб-разработкой, может уподобить его столь же печально известному продукту Microsoft.) Но в ответ на @ S.Lott: я редко пишу код Python для Windows, который не должен также работать в Linux, OSX, BSD и т. Д., Поэтому я честно говоря, не думайте, что «выбрать приоритет» - это полезный совет, тем более что Python абстрагирует различия платформ настолько, насколько это возможно.
Майкл Шепер

Ответы:


163

Отправка сигнала 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 06

8
Теперь поддерживается Windows. docs.python.org/library/os.html?highlight=os.kill#os.kill
Майкл

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()которую вы можете использовать, чтобы проверить, существует ли процесс с данным pid.

Вот пример:

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 означает «нет такого процесса».
Джампаоло Родола,

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

Ты прав. Я отредактировал код, который теперь отражает то, что вы предложили.
Джампаоло Родола

в 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за отказа в разрешении можно отличить от других - либо путем просмотра errno, либо путем перехвата более специализированных PermissionError/ ProcessLookupErrorисключений, которые происходят из OSError. Более того, вы получите ошибку разрешения, только если процесс существует. Таким образом, ваш пример - это просто альтернативный метод, который работает в Linux и некоторых других Unix, но он не более полон, чем правильный вызов os.kill(pid, 0).
maxschlepzig 06

Это решение не является корсс-платформой. OP хочет, чтобы он был доступен в Unix и Windows. В /procPROCFS выходит только на Linux, даже не на BSD или OSX.
Чарльз

Этот метод не работает, если / proc смонтирован hidepid = 2, процесс не будет отображаться в списке, если он принадлежит другому пользователю.
Perkins

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 по этому списку. Я понятия не имею о стоимости производительности, поэтому вам лучше проверить это, если вы собираетесь часто выполнять проверку 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дополнительно проверить код выхода.
Meow

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; в противном случае вы вернете False. Документация для 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}. Вместо выполнения одного системного вызова ваш код разветвляет дочерний элемент, запускает оболочку в этом дочернем элементе, оболочка интерпретирует ваш лишний мини-сценарий оболочки, оболочка создает другой дочерний элемент, который выполняет pgrep, и, наконец, pgrep выполняет итерацию /proc. Ваш ответ не отвечает на заданный вопрос. OP запросил метод с учетом PID. Вашему методу требуется имя процесса.
maxschlepzig 06

0

Следующий код работает как в Linux, так и в Windows, и не зависит от внешних модулей.

import os
import subprocess
import platform
import re

def pid_alive(pid:int):
    """ Check For whether a pid is alive """


    system = platform.uname().system
    if re.search('Linux', system, re.IGNORECASE):
        try:
            os.kill(pid, 0)
        except OSError:
            return False
        else:
            return True
    elif re.search('Windows', system, re.IGNORECASE):
        out = subprocess.check_output(["tasklist","/fi",f"PID eq {pid}"]).strip()
        # b'INFO: No tasks are running which match the specified criteria.'

        if re.search(b'No tasks', out, re.IGNORECASE):
            return False
        else:
            return True
    else:
        raise RuntimeError(f"unsupported system={system}")

Его можно легко улучшить, если вам понадобится

  1. другие платформы
  2. другой язык

-2

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


Я должен был быть более конкретным - проверяю на НЕДЕЙСТВИТЕЛЬНОСТЬ. Итак, я в основном хочу видеть, НЕ используется ли pid.
Эван Фосмарк,

1
Но что вы будете делать с этим ответом? В тот момент, когда вы получили эти знания, кто-то может использовать этот pid.
Damien_The_Unbeliever

@Damien_The_Unbeliever - это нормально, если что-то использует его после того, как я получил эти знания, и я понимаю, что вы говорите о состоянии гонки, но я могу заверить вас, что это не относится к моей ситуации.
Эван Фосмарк,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.