Jak sprawdzić, czy w Pythonie istnieje proces z podanym pidem?

109

Czy istnieje sposób, aby sprawdzić, czy pid odpowiada ważnemu procesowi? Otrzymuję pid z innego źródła niż z os.getpid()i muszę sprawdzić, czy na komputerze nie ma procesu z tym identyfikatorem.

Potrzebuję tego, aby był dostępny w systemach Unix i Windows. Sprawdzam również, czy PID NIE jest używany.

Evan Fosmark
źródło
2
Windows to niestandardowy system operacyjny. Tego typu rzeczy NIE są przenośne. Wiedząc, że nie możesz mieć obu, co jest Twoim priorytetem? Wybierz jeden jako priorytet i edytuj pytanie.
S.Lott,
26
@ S.Lott Windows jest niestandardowa OS Jest to jeden z najbardziej głupie uwagi widziałem na taki ...
Piotr Dobrogost
2
@Piotr Dobrogost: Czy możesz dostarczyć kod obsługujący standardowe systemy UNIX POSIX i systemy Windows inne niż POSIX? Jeśli tak, podaj odpowiedź, która (a) rozwiązuje problem i (b) wyjaśnia, że ​​Windows jest w jakiś sposób zgodny ze standardem POSIX.
S.Lott,
3
@PiotrDobrogost Myślę, że uwaga S. Lotta dotyczyła bardziej szczegółów implementacji i obsługi API niż udziału w rynku.
Roy Tinker
3
Windows z pewnością ma mniej wspólnego z innymi popularnymi systemami operacyjnymi niż pozostałe. (Każdy, kto zajmuje się tworzeniem stron internetowych, może porównać to do podobnie niesławnego produktu firmy Microsoft.) Ale w odpowiedzi na @ S.Lott: Rzadko piszę kod Pythona dla systemu Windows, który nie powinien działać również na Linuksie, OSX, BSD itd. Szczerze mówiąc, nie myśl, że „wybierz priorytet” to pomocna rada, zwłaszcza że Python uściśla różnice między platformami tak bardzo, jak to tylko możliwe.
Michael Scheper,

Odpowiedzi:

163

Wysłanie sygnału 0 do pidu zgłosi wyjątek OSError, jeśli pid nie jest uruchomiony, i nie zrobi nic innego.

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
mluebke
źródło
3
Na pewno działa w Linuksie i OSX, nie mogę mówić w Windowsie. Nie zabija procesu w tym sensie, o jaki pytasz, wysyła sygnał 0, który w zasadzie brzmi „Czy biegniesz?”.
mluebke
10
To zdecydowanie nie działa w systemie Windows, ponieważ nie ma czegoś takiego jak sygnały podobne do UNIX.
Alexander Lebedev,
13
Aby być kompletnym, powinieneś również sprawdzić numer błędu, aby upewnić się, że jest to 3 (złap wyjątek i sprawdź pierwszy argument). Jest to ważne, jeśli proces docelowy istnieje, ale nie masz uprawnień do wysyłania sygnału (z jakiegokolwiek powodu).
haridsv
8
Obsługiwane teraz przez Windows. docs.python.org/library/os.html?highlight=os.kill#os.kill
michael
15
os.kill jest obsługiwany w systemie Windows, ale os.kill(pid, 0)działa tak samo, jak os.kill(pid, signal.CTRL_C_EVENT)może zakończyć proces (lub zakończyć się niepowodzeniem). Dostaję OSErrorgdzie, errno==EINVALkiedy próbuję tego w podprocesie.
Jason R. Coombs,
76

Spójrz na psutilmoduł:

psutil (Python System and Process Utilities) to wieloplatformowa biblioteka do pobierania informacji o uruchomionych procesach i wykorzystaniu systemu (procesor, pamięć, dyski, sieć) w Pythonie. [...] Obecnie obsługuje Linux , Windows , OSX , FreeBSD i Sun Solaris , zarówno architekturę 32-bitową, jak i 64-bitową , z wersjami Pythona od 2.6 do 3.4 (użytkownicy Pythona 2.4 i 2.5 mogą używać wersji 2.1.3) . PyPy jest również znany z działania.

Posiada funkcję o nazwie pid_exists(), której możesz użyć do sprawdzenia, czy istnieje proces o podanym pid.

Oto przykład:

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)

Na przykład:

moooeeeep
źródło
Miałem przypadek, w którym pid_exists przestał być niezawodny w systemie Windows. Błędne zgłaszanie martwych pidów jako używanych. Jest to przypomnienie, aby od czasu do czasu aktualizować moduł psutil - szczególnie podczas uaktualniania systemu operacyjnego.
Damon Brodie
62

kod mluebke nie jest w 100% poprawny; kill () może również wywołać EPERM (odmowa dostępu), w którym to przypadku oznacza to oczywiście, że proces istnieje. To ma zadziałać:

(zredagowane zgodnie z komentarzami Jason R. Coombs)

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

Nie możesz tego zrobić w systemie Windows, chyba że używasz pywin32, ctypes lub modułu rozszerzenia C. Jeśli nie masz nic przeciwko uzależnieniu się od zewnętrznej biblioteki, możesz użyć psutil :

>>> import psutil
>>> psutil.pid_exists(2353)
True
Giampaolo Rodolà
źródło
haridsv sugeruje, że test powinien być e.errno! = 3; prawdopodobnie e.errno! = errno.ESRCH
Jason R. Coombs
Czemu? ESRCH oznacza „nie ma takiego procesu”.
Giampaolo Rodolà
2
Dobrze. Ponieważ ESRCH oznacza „nie ma takiego procesu”, errno! = ESRCH oznacza „nie ma takiego procesu” lub „proces istnieje”, co jest bardzo podobne do nazwy funkcji. Wspomniałeś konkretnie o EPERM, ale co oznaczają inne możliwe kody błędów? Wydaje się niewłaściwe wyodrębnienie kodu błędu, który jest luźno powiązany z celem sprawdzenia, podczas gdy ESRCH wydaje się być blisko powiązany.
Jason R. Coombs
Masz rację. Edytowałem kod, który teraz odzwierciedla to, co zasugerowałeś.
Giampaolo Rodolà
w python3 os.kill () rzuca ProcessLookupError
martinkunev
19

Odpowiedzi obejmujące wysłanie „sygnału 0” do procesu będą działać tylko wtedy, gdy właścicielem danego procesu jest użytkownik uruchamiający test . W przeciwnym razie otrzymasz uprawnieniaOSError z powodu uprawnień , nawet jeśli pid istnieje w systemie.

Aby ominąć to ograniczenie, możesz sprawdzić, czy /proc/<pid>istnieje:

import os

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

Dotyczy to oczywiście tylko systemów opartych na Linuksie.

Omer Dagan
źródło
źle. PermissionErroroznacza , że pid istnieje , otrzymasz, ProcessLookupErrorjeśli pid nie istnieje.
jfs
Przyczyną OSErrorodmowy zgody można odróżnić od innych - patrząc na errno lub wychwytując bardziej wyspecjalizowane PermissionError/ ProcessLookupErrorwyjątki, z których wynikają OSError. Co więcej, błąd uprawnień pojawia się tylko wtedy, gdy proces istnieje. Zatem twój przykład jest po prostu alternatywną metodą, która działa na Linuksie i niektórych innych Uniksach, ale nie jest bardziej kompletna niż prawidłowe wywołanie os.kill(pid, 0).
maxschlepzig
To rozwiązanie nie jest platformą corss. OP chce, aby był dostępny w systemach Unix i Windows. W /procprocfs wychodzi tylko na Linuksie, nawet nie na BSD lub OSX.
Charles
Ta metoda nie powiedzie się, jeśli / proc jest zamontowany hidepid = 2, proces nie zostanie wyświetlony na liście, jeśli należy do innego użytkownika.
Perkins
8

W Pythonie 3.3+ można używać nazw wyjątków zamiast stałych errno. Wersja 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
jfs
źródło
7

Poszukaj tutaj specyficznego dla systemu Windows sposobu uzyskania pełnej listy uruchomionych procesów wraz z ich identyfikatorami. To byłoby coś takiego

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]

Następnie możesz zweryfikować pid, który otrzymałeś na tej liście. Nie mam pojęcia o kosztach wydajności, więc lepiej sprawdź to, jeśli masz zamiar często przeprowadzać weryfikację pid.

W przypadku * NIx wystarczy użyć roztworu mluebke.

Alexander Lebedev
źródło
To działało dobrze dla mnie. Chciałem sprawdzić nazwę procesu, więc zamieniłem „ProcessID” na „Name”, a także przekonwertowałem je na check for in list, aby zwracało True or False.
JamesR
6

Opierając się na ntrrgc, wzmocniłem wersję systemu Windows, aby sprawdzała kod zakończenia procesu i szukała uprawnień:

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
speedplane
źródło
Właściwie, zgodnie z msdn.microsoft.com/en-us/library/windows/desktop/… , GetExistCodeProcesswymagane są prawa dostępu PROCESS_QUERY_INFORMATIONi PROCESS_QUERY_LIMITED_INFORMATION.
augustomen
Zwróć uwagę, że kod jest nieprawidłowy. GetExitCodeProcessotrzymuje uchwyt i wskaźnik, aw tym przykładzie otrzymuje ExitCodeProcessstrukturę jako drugi parametr, podczas gdy powinien to być tylko wskaźnik.
Fabio Zadrozny
Po OpenProcess warto sprawdzić GetLastError. ERROR_ACCESS_DENIED oznacza, że ​​proces istnieje! Oto pełny przykład wykorzystujący to: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule
4

Łącząc odpowiedź Giampaolo Rodolà dla POSIX i moją dla Windows , otrzymałem to:

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
Alicia
źródło
Wersja dla Windows nie działa dla mnie na Windows 8.1. Musisz sprawdzić GetExitCodeProcessi upewnić się, że masz nawet dostęp.
szybowiec
kernel32.OpenProcessSamo używanie nie wystarczy. Jak wskazano tutaj , „jeśli proces zakończył się niedawno, pid może nadal istnieć dla uchwytu”. Jeśli kernel32.OpenProcesszwraca wartość różną od zera, nadal musimy kernel32.GetExitCodeProcesssprawdzić kod zakończenia.
Meow
2

W systemie Windows możesz to zrobić w następujący sposób:

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

Przede wszystkim w tym kodzie próbujesz uzyskać uchwyt dla procesu z podanym pid. Jeśli uchwyt jest prawidłowy, zamknij uchwyt dla procesu i zwróć True; w przeciwnym razie zwrócisz False. Dokumentacja dla OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx

Andrei
źródło
2

To zadziała na Linuksie, na przykład, jeśli chcesz sprawdzić, czy banshee działa ... (banshee to odtwarzacz muzyki)

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")
BARSPIN
źródło
Ta metoda jest zdecydowanie gorsza w porównaniu do używania os.kill(pid, 0)lub patrzenia /proc/{pid}. Zamiast wykonywać jedno wywołanie systemowe, twój kod rozwidla dziecko, wykonuje powłokę w tym dziecku, powłoka interpretuje twój zbędny mini-skrypt powłoki, powłoka rozwidla kolejne dziecko, które wykonuje pgrep i na końcu iteruje pgrep /proc. Twoja odpowiedź nie odpowiada na opublikowane pytanie. OP poprosił o metodę z podanym PID. Twoja metoda wymaga nazwy procesu.
maxschlepzig
0

Poniższy kod działa zarówno w systemie Linux, jak i Windows i nie zależy od modułów zewnętrznych

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}")

W razie potrzeby można go łatwo ulepszyć

  1. inne platformy
  2. inny język
oldpride
źródło
-2

Powiedziałbym, że używaj PID w jakimkolwiek celu, w jakim go otrzymujesz i z wdziękiem radzisz sobie z błędami. W przeciwnym razie jest to wyścig klasyczny (PID może być ważny, gdy sprawdzisz, czy jest ważny, ale odejdź natychmiast później)

Damien_The_Unbeliever
źródło
Powinienem był być bardziej szczegółowy - sprawdzam NIEWAŻNOŚĆ. Więc zasadniczo chcę być w stanie zobaczyć, czy pid NIE jest używany.
Evan Fosmark
1
Ale co zrobisz z tą odpowiedzią? Zaraz po zdobyciu tej wiedzy coś może użyć tego pidu.
Damien_The_Unbeliever,
@Damien_The_Unbeliever - to w porządku, jeśli coś go używa po tym, jak zdobędę tę wiedzę i rozumiem, co mówisz o stanie wyścigu, ale mogę Cię zapewnić, że nie dotyczy to mojej sytuacji.
Evan Fosmark