Wie kann ich überprüfen, ob in Python ein Prozess mit einer bestimmten PID vorhanden ist?

109

Gibt es eine Möglichkeit zu überprüfen, ob eine PID einem gültigen Prozess entspricht? Ich erhalte eine PID von einer anderen Quelle als von os.getpid()und muss überprüfen, ob auf dem Computer kein Prozess mit dieser PID vorhanden ist.

Ich brauche es, um unter Unix und Windows verfügbar zu sein. Ich überprüfe auch, ob die PID NICHT verwendet wird.

Evan Fosmark
quelle
2
Windows ist ein nicht standardmäßiges Betriebssystem. Diese Art von Dingen sind NICHT tragbar. Zu wissen, dass Sie nicht beides haben können, was ist Ihre Priorität? Wählen Sie eine als Priorität und bearbeiten Sie die Frage.
S.Lott
26
@ S.Lott Windows ist ein nicht standardmäßiges Betriebssystem Dies ist eine der albernsten Bemerkungen, die ich auf SO gesehen habe ...
Piotr Dobrogost
2
@Piotr Dobrogost: Können Sie Code bereitstellen, der POSIX-Standard-Unix- und Nicht-POSIX-Standard-Windows verarbeitet? Wenn ja, geben Sie bitte eine Antwort, die (a) das Problem löst und (b) deutlich macht, dass Windows irgendwie mit dem POSIX-Standard kompatibel ist.
S.Lott
3
@PiotrDobrogost Ich denke, S.Lotts Bemerkung betraf mehr Implementierungsdetails und API-Unterstützung als Marktanteile.
Roy Tinker
3
Windows hat sicherlich weniger mit anderen gängigen Betriebssystemen gemeinsam als die anderen. (Jeder, der Webentwicklung betreibt, kann es mit einem ähnlich berüchtigten Microsoft-Produkt vergleichen.) Aber als Antwort auf @ S.Lott: Ich schreibe selten Python-Code für Windows, der nicht auch unter Linux, OSX, BSD usw. funktionieren soll Denken Sie ehrlich gesagt nicht, dass "Auswahl als Priorität" ein hilfreicher Rat ist, zumal Python Plattformunterschiede so weit wie möglich entfernt.
Michael Scheper

Antworten:

162

Das Senden des Signals 0 an eine PID löst eine OSError-Ausnahme aus, wenn die PID nicht ausgeführt wird, und führt nichts anderes aus.

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
quelle
3
Funktioniert sicher unter Linux und OSX, ich kann nicht für Windows sprechen. Es beendet den Prozess nicht in dem Sinne, dass Sie fragen, es sendet das Signal 0, das im Grunde "Laufen Sie?" Ist.
mluebke
10
Dies funktioniert definitiv nicht unter Windows, da es keine UNIX-ähnlichen Signale gibt.
Alexander Lebedev
13
Um vollständig zu sein, sollten Sie auch nach der Fehlernummer suchen, um sicherzustellen, dass sie 3 ist (fangen Sie die Ausnahme ab und suchen Sie nach dem ersten Argument). Dies ist wichtig, wenn der Zielprozess vorhanden ist, Sie jedoch keine Berechtigung zum Senden eines Signals haben (aus welchem ​​Grund auch immer).
Haridsv
8
Wird jetzt von Windows unterstützt. docs.python.org/library/os.html?highlight=os.kill#os.kill
Michael
15
os.kill wird unter Windows unterstützt, aber os.kill(pid, 0)ist das gleiche wie os.kill(pid, signal.CTRL_C_EVENT)die den Prozess beenden kann (oder nicht). Ich bekomme ein OSErrorWo, errno==EINVALwenn ich dies in einem Unterprozess versuche.
Jason R. Coombs
76

Schauen Sie sich das psutilModul an:

psutil (Python-System- und Prozessdienstprogramme) ist eine plattformübergreifende Bibliothek zum Abrufen von Informationen zum Ausführen von Prozessen und zur Systemauslastung (CPU, Speicher, Festplatten, Netzwerk) in Python. [...] Es unterstützt derzeit Linux , Windows , OSX , FreeBSD und Sun Solaris , sowohl 32-Bit- als auch 64-Bit- Architekturen, mit Python-Versionen von 2.6 bis 3.4 (Benutzer von Python 2.4 und 2.5 können die Version 2.1.3 verwenden). . Es ist auch bekannt, dass PyPy funktioniert.

Es hat eine Funktion namens pid_exists(), mit der Sie überprüfen können, ob ein Prozess mit der angegebenen PID vorhanden ist.

Hier ist ein Beispiel:

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)

Als Referenz:

moooeeeep
quelle
Ich hatte eine Instanz, in der pid_exists unter Windows nicht mehr zuverlässig war. Tote Pids wurden fälschlicherweise als in Gebrauch gemeldet. Dies ist eine Erinnerung daran, Ihr psutil-Modul von Zeit zu Zeit auf dem neuesten Stand zu halten - insbesondere bei Betriebssystem-Upgrades.
Damon Brodie
62

mluebke Code ist nicht 100% korrekt; kill () kann auch EPERM (Zugriff verweigert) auslösen. In diesem Fall bedeutet dies offensichtlich, dass ein Prozess vorhanden ist. Das soll funktionieren:

(bearbeitet gemäß den Kommentaren von 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

Sie können dies unter Windows nur tun, wenn Sie pywin32, ctypes oder ein C-Erweiterungsmodul verwenden. Wenn Sie mit der Abhängigkeit von einer externen Bibliothek einverstanden sind, können Sie psutil verwenden :

>>> import psutil
>>> psutil.pid_exists(2353)
True
Giampaolo Rodolà
quelle
haridsv schlägt vor, dass der Test e.errno sein sollte! = 3; vielleicht e.errno! = errno.ESRCH
Jason R. Coombs
Warum? ESRCH bedeutet "kein solcher Prozess".
Giampaolo Rodolà
2
Richtig. Da ESRCH "kein solcher Prozess" bedeutet, bedeutet errno! = ESRCH "kein solcher Prozess" oder "Prozess existiert", was dem Namen der Funktion sehr ähnlich ist. Sie haben speziell EPERM erwähnt, aber was bedeuten die anderen möglichen Fehlercodes? Es erscheint falsch, einen Fehlercode herauszusuchen, der in engem Zusammenhang mit der Absicht der Prüfung steht, während ESRCH eng miteinander verbunden zu sein scheint.
Jason R. Coombs
Du hast recht. Ich habe den Code bearbeitet, der jetzt Ihren Vorschlägen entspricht.
Giampaolo Rodolà
in python3 os.kill () wirft ProcessLookupError
martinkunev
19

Die Antworten zum Senden von 'Signal 0' an den Prozess funktionieren nur, wenn der betreffende Prozess dem Benutzer gehört, der den Test ausführt . Andernfalls erhalten Sie OSErroraufgrund von Berechtigungen eine , auch wenn die PID im System vorhanden ist.

Um diese Einschränkung zu umgehen, können Sie überprüfen, ob /proc/<pid>Folgendes vorhanden ist:

import os

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

Dies gilt natürlich nur für Linux-basierte Systeme.

Omer Dagan
quelle
falsch. PermissionErrorbedeutet, dass pid existiert , erhalten Sie, ProcessLookupErrorwenn pid nicht existiert.
JFS
Die OSErroraufgrund der verweigerten Erlaubnis kann von anderen unterschieden werden - entweder durch Betrachten von errno oder durch Abfangen der spezialisierteren PermissionError/ ProcessLookupErrorAusnahmen, die sich daraus ergeben OSError. Darüber hinaus erhalten Sie den Berechtigungsfehler nur, wenn der Prozess vorhanden ist. Daher ist Ihr Beispiel nur eine alternative Methode, die unter Linux und einigen anderen Unices funktioniert, aber nicht vollständiger ist als ein ordnungsgemäßer Aufruf os.kill(pid, 0).
Maxschlepzig
Diese Lösung ist keine Corss-Plattform. Das OP möchte, dass es unter Unix und Windows verfügbar ist. Der /procProzess wird nur unter Linux beendet, nicht einmal unter BSD oder OSX.
Charles
Diese Methode schlägt fehl, wenn / proc hidepid = 2 aktiviert ist. Der Prozess wird nicht in der Liste angezeigt, wenn er einem anderen Benutzer gehört.
Perkins
8

In Python 3.3+ können Sie Ausnahmenamen anstelle von errno-Konstanten verwenden. Posix-Version :

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
quelle
7

Suchen Sie hier nach einer Windows-spezifischen Methode, um eine vollständige Liste der ausgeführten Prozesse mit ihren IDs abzurufen. Es wäre so etwas wie

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]

Sie können dann überprüfen, welche PID Sie für diese Liste erhalten. Ich habe keine Ahnung von den Leistungskosten, daher sollten Sie dies überprüfen, wenn Sie häufig eine PID-Überprüfung durchführen.

Verwenden Sie für * NIx einfach die Lösung von mluebke.

Alexander Lebedev
quelle
Das hat bei mir gut funktioniert. Ich wollte nach einem Prozessnamen suchen, also tauschte ich "ProcessID" gegen "Name" aus und wandelte ihn auch in eine Check-in-Liste um, um True oder False zurückzugeben.
JamesR
6

Aufbauend auf ntrrgc habe ich die Windows-Version verbessert, damit der Prozess-Exit-Code und die Berechtigungen überprüft werden:

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
Geschwindigkeitsflugzeug
quelle
Eigentlich nach msdn.microsoft.com/en-us/library/windows/desktop/... , GetExistCodeProcesserfordert die PROCESS_QUERY_INFORMATIONund PROCESS_QUERY_LIMITED_INFORMATIONZugriffsrechte.
Augustomen
Beachten Sie, dass der Code falsch ist. GetExitCodeProcessempfängt ein Handle und einen Zeiger und in diesem Beispiel erhält es eine ExitCodeProcessStruktur als zweiten Parameter, wenn es nur ein Zeiger sein sollte.
Fabio Zadrozny
Nach OpenProcess empfiehlt es sich, GetLastError zu überprüfen. Ein ERROR_ACCESS_DENIED bedeutet, dass der Prozess existiert! Hier ist ein vollständiges Beispiel, das dies verwendet: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule
4

Durch die Kombination von Giampaolo Rodolàs Antwort für POSIX und meiner für Windows habe ich Folgendes erhalten :

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
quelle
Die Windows-Version funktioniert bei Windows 8.1 nicht. Sie müssen überprüfen GetExitCodeProcessund sicherstellen, dass Sie überhaupt Zugriff haben.
Speedplane
Nur zu verwenden kernel32.OpenProcessist nicht genug. Wie hier ausgeführt : "Wenn der Prozess kürzlich beendet wurde, ist möglicherweise noch eine PID für das Handle vorhanden." Wenn ein kernel32.OpenProcessWert ungleich Null zurückgegeben wird, müssen wir kernel32.GetExitCodeProcessden Exit-Code noch weiter überprüfen.
Miau
2

In Windows können Sie dies folgendermaßen tun:

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

Zunächst versuchen Sie in diesem Code, ein Handle für den Prozess mit der angegebenen PID zu erhalten. Wenn das Handle gültig ist, schließen Sie das Handle für den Prozess und geben Sie True zurück. Andernfalls geben Sie False zurück. Dokumentation für OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx

Andrei
quelle
2

Dies funktioniert unter Linux, wenn Sie beispielsweise überprüfen möchten, ob Banshee ausgeführt wird ... (Banshee ist ein Musikplayer)

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
quelle
Diese Methode ist eindeutig schlechter als die Verwendung os.kill(pid, 0)oder Betrachtung /proc/{pid}. Anstatt einen Systemaufruf auszuführen, gibt Ihr Code ein Kind aus, führt eine Shell in diesem Kind aus, die Shell interpretiert Ihr überflüssiges Mini-Shell-Skript, die Shell gibt ein anderes Kind aus, das pgrep ausführt und schließlich pgrep wiederholt /proc. Ihre Antwort beantwortet die gestellte Frage nicht. Das OP fragte nach einer Methode mit einer PID. Ihre Methode erfordert einen Prozessnamen.
Maxschlepzig
-2

Ich würde sagen, verwenden Sie die PID für jeden Zweck, den Sie erhalten, und behandeln Sie die Fehler ordnungsgemäß. Ansonsten ist es ein klassisches Rennen (die PID kann gültig sein, wenn Sie überprüfen, ob sie gültig ist, aber einen Moment später verschwinden)

Damien_The_Unbeliever
quelle
Ich hätte genauer sein sollen - ich überprüfe die UNGÜLTIGKEIT. Ich möchte also im Grunde sehen können, ob eine PID NICHT verwendet wird.
Evan Fosmark
1
Aber was machen Sie mit dieser Antwort? In dem Moment, in dem Sie dieses Wissen erworben haben, könnte etwas diese PID verwenden.
Damien_The_Unbeliever
@Damien_The_Unbeliever - das ist in Ordnung, wenn etwas es verwendet, nachdem ich dieses Wissen erlangt habe, und ich verstehe, was Sie über die Rennbedingungen sagen, aber ich kann Ihnen versichern, dass es für meine Situation nicht gilt.
Evan Fosmark