Как проверить, существует ли процесс с данным pid в Python?
Есть ли способ проверить, соответствует ли pid действительному процессу? Я получаю pid из другого источника, кроме как из os.getpid()
и мне нужно проверить, не существует ли на машине процесс с этим pid.
Мне нужно, чтобы он был доступен в Unix и Windows. Я также проверяю, не используется ли PID.
15 ответов
Отправка сигнала 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
Посмотрите на psutil
модуль:
psutil (Python System и утилиты процессов) - это кроссплатформенная библиотека для получения информации о запущенных процессах и использовании системы (CPU, память, диски, сеть) в 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
Для справки:
Код млюбке не на 100% корректен; kill() также может вызывать EPERM (доступ запрещен), в этом случае это, очевидно, означает, что процесс существует. Это должно работать:
(отредактировано в соответствии с комментариями Джейсона Р. Кумбса)
import errno
import os
import sys
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
Ответы, включающие отправку "сигнала 0" процессу, будут работать только в том случае, если рассматриваемый процесс принадлежит пользователю, выполняющему тест. В противном случае вы получите OSError
из-за разрешений, даже если pid существует в системе.
Чтобы обойти это ограничение, вы можете проверить, если /proc/<pid>
существует:
import os
def is_running(pid):
if os.path.isdir('/proc/{}'.format(pid)):
return True
return False
Это относится только к системам на основе Linux, очевидно.
В 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
Ищите здесь конкретный способ получения полного списка запущенных процессов с их идентификаторами. Это было бы что-то вроде
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.
Основываясь на 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
Объединив ответ Джампаоло Родолы для 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
Это будет работать для 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")
Попробовав разные решения, полагаться на psutil было лучшим независимым от ОС способом точно определить, существует ли PID. Как было предложено выше,psutil.pid_exists(pid)
работает по назначению, но недостатком является то, что он включает устаревшие идентификаторы процессов и процессы, которые были недавно завершены.
Решение: проверьте, существует ли идентификатор процесса и работает ли он активно.
import psutil
def process_active(pid: int or str) -> bool:
try:
process = psutil.Process(pid)
except psutil.Error as error: # includes NoSuchProcess error
return False
if psutil.pid_exists(pid) and process.status() == psutil.STATUS_RUNNING:
return True
В 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
Я обнаружил, что это решение хорошо работает как в Windows, так и в Linux. Я использовал psutil для проверки.
import psutil
import subprocess
import os
p = subprocess.Popen(['python', self.evaluation_script],stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
pid = p.pid
def __check_process_running__(self,p):
if p is not None:
poll = p.poll()
if poll == None:
return True
return False
def __check_PID_running__(self,pid):
"""
Checks if a pid is still running (UNIX works, windows we'll see)
Inputs:
pid - process id
returns:
True if running, False if not
"""
if (platform.system() == 'Linux'):
try:
os.kill(pid, 0)
if pid<0: # In case code terminates
return False
except OSError:
return False
else:
return True
elif (platform.system() == 'Windows'):
return pid in (p.pid for p in psutil.process_iter())
Следующий код работает как в 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}")
Его можно легко улучшить, если вам понадобится
- другие платформы
- другой язык
Другой вариант для Windows — через пакет pywin32:
pid in win32process.EnumProcesses()
Win32process.EnumProcesses() возвращает PID для запущенных в данный момент процессов.
Я бы сказал, использовать PID для любой цели, которую вы получаете, и корректно обрабатывать ошибки. В противном случае, это классическая гонка (PID может быть действительным, когда вы проверите его, но пропадет через мгновение)