Comment vérifier s'il existe un processus avec un pid donné en Python?

109

Existe-t-il un moyen de vérifier si un pid correspond à un processus valide? J'obtiens un pid d'une autre source que de os.getpid()et je dois vérifier si un processus avec ce pid n'existe pas sur la machine.

J'en ai besoin pour être disponible sous Unix et Windows. Je vérifie également si le PID n'est PAS utilisé.

Evan Fosmark
la source
2
Windows est un système d'exploitation non standard. Ces types de choses ne sont PAS portables. Sachant que vous ne pouvez pas avoir les deux, quelle est votre priorité? Choisissez-en une comme priorité et modifiez la question.
S.Lott
26
@ S.Lott Windows est un système d'exploitation non standard C'est l'une des remarques les plus stupides que j'ai vues sur SO ...
Piotr Dobrogost
2
@Piotr Dobrogost: Pouvez-vous fournir du code qui gère le standard UNIX POSIX et Windows standard non POSIX? Si tel est le cas, veuillez fournir une réponse qui (a) résout le problème et (b) indique clairement que Windows est en quelque sorte conforme à la norme POSIX.
S.Lott
3
@PiotrDobrogost Je pense que la remarque de S.Lott portait davantage sur les détails de mise en œuvre et le support API que sur la part de marché.
Roy Tinker
3
Windows a certainement moins en commun avec les autres systèmes d'exploitation populaires que les autres. (Quiconque fait du développement Web peut le comparer à un produit Microsoft tout aussi tristement célèbre.) Mais en réponse à @ S.Lott: j'écris rarement du code Python pour Windows qui n'est pas censé fonctionner également sous Linux, OSX, BSD, etc., donc je Honnêtement, ne pensez pas que `` choisir en priorité '' est un conseil utile, d'autant plus que Python résume les différences de plate-forme autant que possible.
Michael Scheper

Réponses:

163

L'envoi du signal 0 à un pid lèvera une exception OSError si le pid n'est pas en cours d'exécution, et ne fera rien autrement.

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
la source
3
Fonctionne à coup sûr sous Linux et OSX, je ne peux pas parler pour Windows. Il ne tue pas le processus dans le sens où vous le demandez, il envoie le signal 0, qui est essentiellement "Êtes-vous en cours d'exécution?".
mluebke
10
Cela ne fonctionne certainement pas sous Windows, car il n'y a pas de signaux de type UNIX.
Alexander Lebedev
13
Pour être complet, vous devez également vérifier le numéro d'erreur pour vous assurer qu'il est 3 (attraper l'exception et vérifier le premier argument). Ceci est important si le processus cible existe mais que vous n'êtes pas autorisé à envoyer un signal (pour une raison quelconque).
haridsv
8
Pris en charge par Windows maintenant. docs.python.org/library/os.html?highlight=os.kill#os.kill
michael
15
os.kill est pris en charge sous Windows, mais os.kill(pid, 0)est le même que celui os.kill(pid, signal.CTRL_C_EVENT)qui peut mettre fin au processus (ou échouer). J'obtiens un OSErrorerrno==EINVALquand j'essaye ceci sur un sous-processus.
Jason R. Coombs
76

Jetez un œil au psutilmodule:

psutil (utilitaires système et de processus python) est une bibliothèque multiplateforme permettant de récupérer des informations sur les processus en cours d' exécution et l' utilisation du système (CPU, mémoire, disques, réseau) en Python. [...] Il prend actuellement en charge Linux , Windows , OSX , FreeBSD et Sun Solaris , à la fois les architectures 32 bits et 64 bits , avec les versions Python de 2.6 à 3.4 (les utilisateurs de Python 2.4 et 2.5 peuvent utiliser la version 2.1.3) . PyPy est également connu pour fonctionner.

Il a une fonction appelée pid_exists()que vous pouvez utiliser pour vérifier si un processus avec le pid donné existe.

Voici un exemple:

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)

Pour référence:

moooeeeep
la source
J'ai eu une instance où pid_exists a cessé d'être fiable sur Windows. Il s'agissait de signaler à tort les pids morts comme étant en cours d'utilisation. Ceci est un rappel pour garder votre module psutil à jour de temps en temps, en particulier lors des mises à niveau du système d'exploitation.
Damon Brodie le
62

le code mluebke n'est pas correct à 100%; kill () peut également lever EPERM (accès refusé) auquel cas cela signifie évidemment qu'un processus existe. Ceci est censé fonctionner:

(édité selon les commentaires de 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

Vous ne pouvez pas faire cela sous Windows à moins d'utiliser pywin32, ctypes ou un module d'extension C. Si vous êtes d'accord avec le fait de dépendre d'une bibliothèque externe, vous pouvez utiliser psutil :

>>> import psutil
>>> psutil.pid_exists(2353)
True
Giampaolo Rodolà
la source
haridsv suggère que le test devrait être e.errno! = 3; peut-être e.errno! = errno.ESRCH
Jason R. Coombs
Pourquoi? ESRCH signifie "aucun processus de ce type".
Giampaolo Rodolà
2
Droite. Puisque ESRCH signifie «aucun processus de ce type», errno! = ESRCH signifie «aucun processus de ce type» ou «processus existe», ce qui est très similaire au nom de la fonction. Vous avez mentionné spécifiquement EPERM, mais qu'impliquent les autres codes d'erreur possibles? Il semble incorrect de distinguer un code d'erreur qui est vaguement lié à l'intention du contrôle, alors que l'ESRCH semble étroitement lié.
Jason R. Coombs
Vous avez raison. J'ai édité le code qui reflète maintenant ce que vous avez suggéré.
Giampaolo Rodolà
dans python3 os.kill () lance ProcessLookupError
martinkunev
19

Les réponses impliquant l'envoi du «signal 0» au processus ne fonctionneront que si le processus en question appartient à l'utilisateur exécutant le test . Sinon, vous obtiendrez un OSErrordû aux autorisations , même si le pid existe dans le système.

Afin de contourner cette limitation, vous pouvez vérifier si elle /proc/<pid>existe:

import os

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

Ceci s'applique aux systèmes Linux uniquement, évidemment.

Omer Dagan
la source
faux. PermissionErrorsignifie que pid existe , vous obtenez ProcessLookupErrorsi pid n'existe pas.
jfs
La OSErrorraison de l'autorisation refusée peut être différenciée des autres - soit en regardant errno, soit en capturant les exceptions PermissionError/ ProcessLookupErrorexceptions plus spécialisées qui en découlent OSError. De plus, vous n'obtenez l'erreur d'autorisation que si le processus existe. Ainsi, votre exemple n'est qu'une méthode alternative qui fonctionne sur Linux et certains autres Unices, mais ce n'est pas plus complet que d'appeler correctement os.kill(pid, 0).
maxschlepzig
Cette solution n'est pas corss-platform. L'OP veut qu'il soit disponible sous Unix et Windows. Le /procprocfs ne se ferme que sous Linux, pas même sous BSD ou OSX.
Charles
Cette méthode échoue si / proc est monté hidepid = 2, le processus n'apparaîtra pas dans la liste s'il appartient à un autre utilisateur.
Perkins
8

Dans Python 3.3+, vous pouvez utiliser des noms d'exception au lieu de constantes errno. Version 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
la source
7

Recherchez ici un moyen spécifique à Windows d'obtenir la liste complète des processus en cours d'exécution avec leurs ID. Ce serait quelque chose comme

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]

Vous pouvez ensuite vérifier le pid obtenu par rapport à cette liste. Je n'ai aucune idée du coût des performances, vous feriez donc mieux de vérifier cela si vous prévoyez d'effectuer souvent une vérification pid.

Pour * NIx, utilisez simplement la solution de mluebke.

Alexandre Lebedev
la source
Cela a bien fonctionné pour moi. Je voulais vérifier un nom de processus ainsi échangé "ProcessID" pour "Nom" et l'ai également converti en une vérification dans la liste pour retourner Vrai ou Faux.
JamesR
6

En m'appuyant sur ntrrgc, j'ai renforcé la version Windows afin qu'elle vérifie le code de sortie du processus et vérifie les autorisations:

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
la source
En fait, selon msdn.microsoft.com/en-us/library/windows/desktop/… , GetExistCodeProcessnécessite les droits d'accès PROCESS_QUERY_INFORMATIONet PROCESS_QUERY_LIMITED_INFORMATION.
augustomen
Notez que le code est incorrect. GetExitCodeProcessreçoit une poignée et un pointeur et dans cet exemple, il reçoit une ExitCodeProcessstructure comme deuxième paramètre alors qu'il ne devrait s'agir que d'un pointeur.
Fabio Zadrozny
Après OpenProcess, c'est une bonne idée de vérifier GetLastError. Un ERROR_ACCESS_DENIED là signifie que le processus existe! Voici un exemple complet utilisant ceci: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule
4

En combinant la réponse de Giampaolo Rodolà pour POSIX et la mienne pour Windows, j'ai obtenu ceci:

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
la source
La version Windows ne fonctionne pas pour moi sous Windows 8.1. Vous devez vérifier GetExitCodeProcesset vous assurer que vous avez même accès.
speedplane
Utiliser kernel32.OpenProcessuniquement ne suffit pas. Comme indiqué ici "si le processus s'est arrêté récemment, un pid peut encore exister pour le handle." Si kernel32.OpenProcessretourne une valeur non nulle, nous devons kernel32.GetExitCodeProcessencore vérifier le code de sortie.
Meow
2

Sous Windows, vous pouvez le faire de cette manière:

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

Tout d'abord, dans ce code, vous essayez d'obtenir une poignée pour le processus avec pid donné. Si le handle est valide, fermez le handle du processus et renvoyez True; sinon, vous retournez False. Documentation pour OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx

Andrei
la source
2

Cela fonctionnera pour Linux, par exemple si vous voulez vérifier si banshee est en cours d'exécution ... (banshee est un lecteur de musique)

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
la source
Cette méthode est clairement inférieure à l'utilisation os.kill(pid, 0)ou à la visualisation /proc/{pid}. Au lieu d'exécuter un appel système, votre code forque un enfant, exécute un shell dans cet enfant, le shell interprète votre mini-script shell superflu, le shell forge un autre enfant qui exécute pgrep et enfin pgrep itère /proc. Votre réponse ne répond pas à la question posée. Le PO a demandé une méthode avec un PID. Votre méthode nécessite un nom de processus.
maxschlepzig
0

Le code suivant fonctionne à la fois sous Linux et Windows, et ne dépend pas des modules externes

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

Il peut être facilement amélioré au cas où vous auriez besoin

  1. autres plateformes
  2. autre langue
oldpride
la source
-2

Je dirais que vous utilisez le PID dans tous les cas où vous l'obtenez et gérez les erreurs avec élégance. Sinon, c'est une course classique (le PID peut être valide lorsque vous le vérifiez, mais disparaît un instant plus tard)

Damien_The_Unbeliever
la source
J'aurais dû être plus précis - je vérifie l'INVALIDITÉ. Donc, je veux essentiellement pouvoir voir si un pid n'est PAS utilisé.
Evan Fosmark
1
Mais que ferez-vous de cette réponse? Dès l'instant où vous avez acquis cette connaissance, quelque chose pourrait utiliser ce pid.
Damien_The_Unbeliever
@Damien_The_Unbeliever - ce n'est pas grave si quelque chose l'utilise après avoir acquis ces connaissances, et je comprends ce que vous dites à propos de la condition de concurrence, mais je peux vous assurer que cela ne s'applique pas à ma situation.
Evan Fosmark