¿Cómo comprobar si existe un proceso con un pid determinado en Python?

109

¿Hay alguna forma de comprobar si un pid corresponde a un proceso válido? Recibo un pid de una fuente diferente a la de os.getpid()y necesito verificar si un proceso con ese pid no existe en la máquina.

Necesito que esté disponible en Unix y Windows. También estoy comprobando si el PID NO está en uso.

Evan Fosmark
fuente
2
Windows es un sistema operativo no estándar. Este tipo de cosas NO son portátiles. Sabiendo que no puede tener ambos, ¿cuál es su prioridad? Elija uno como prioridad y edite la pregunta.
S.Lott
26
@ S.Lott Windows es un sistema operativo no estándar Este es uno de los comentarios más tontos que he visto en SO ...
Piotr Dobrogost
2
@Piotr Dobrogost: ¿Puede proporcionar código que maneje unix estándar POSIX y Windows estándar no POSIX? Si es así, proporcione una respuesta que (a) resuelva el problema y (b) deje en claro que Windows de alguna manera cumple con el estándar POSIX.
S.Lott
3
@PiotrDobrogost Creo que el comentario de S.Lott fue más sobre detalles de implementación y soporte de API que sobre participación de mercado.
Roy Tinker
3
Windows ciertamente tiene menos en común con otros sistemas operativos populares que el resto entre sí. (Cualquiera que haga desarrollo web puede compararlo con un producto de Microsoft igualmente infame). Pero en respuesta a @ S.Lott: Rara vez escribo código Python para Windows que se supone que no funciona también en Linux, OSX, BSD, etc. Honestamente, no creo que 'elegir como una prioridad' sea un consejo útil, especialmente porque Python abstrae las diferencias de plataforma tanto como sea posible.
Michael Scheper

Respuestas:

162

El envío de la señal 0 a un pid generará una excepción OSError si el pid no se está ejecutando y no hará nada de lo contrario.

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
fuente
3
Seguro que funciona en Linux y OSX, no puedo hablar por Windows. No mata el proceso en el sentido que estás preguntando, envía la señal 0, que es básicamente "¿Estás corriendo?".
mluebke
10
Esto definitivamente no funciona en Windows, ya que no existen señales similares a UNIX.
Alexander Lebedev
13
Para estar completo, también debe verificar el número de error para asegurarse de que sea 3 (capture la excepción y verifique el primer argumento). Esto es importante si el proceso de destino existe pero no tiene permiso para enviar señales (por cualquier motivo).
haridsv
8
Compatible con Windows ahora. docs.python.org/library/os.html?highlight=os.kill#os.kill
michael
15
os.kill es compatible con Windows, pero os.kill(pid, 0)es el mismo os.kill(pid, signal.CTRL_C_EVENT)que puede terminar el proceso (o fallar). Obtengo un OSErrordónde errno==EINVALcuando intento esto en un subproceso.
Jason R. Coombs
76

Eche un vistazo al psutilmódulo:

psutil (utilidades de proceso y sistema de Python) es una biblioteca multiplataforma para recuperar información sobre procesos en ejecución y utilización del sistema (CPU, memoria, discos, red) en Python. [...] Actualmente es compatible con Linux , Windows , OSX , FreeBSD y Sun Solaris , tanto arquitecturas de 32 bits como de 64 bits , con versiones de Python de 2.6 a 3.4 (los usuarios de Python 2.4 y 2.5 pueden usar la versión 2.1.3) . También se sabe que PyPy funciona.

Tiene una función llamada pid_exists()que puede usar para verificar si existe un proceso con el pid dado.

He aquí un ejemplo:

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)

Para referencia:

moooeeeep
fuente
Tuve una instancia en la que pid_exists dejó de ser confiable en Windows. Estaba informando erróneamente que las pids muertas estaban en uso. Este es un recordatorio para mantener actualizado su módulo psutil de vez en cuando, especialmente al realizar actualizaciones del sistema operativo.
Damon Brodie
62

el código de mluebke no es 100% correcto; kill () también puede generar EPERM (acceso denegado) en cuyo caso eso obviamente significa que existe un proceso. Se supone que esto funciona:

(editado según los comentarios 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

No puede hacer esto en Windows a menos que use pywin32, ctypes o un módulo de extensión C. Si está de acuerdo con depender de una biblioteca externa, puede usar psutil :

>>> import psutil
>>> psutil.pid_exists(2353)
True
Giampaolo Rodolà
fuente
haridsv sugiere que la prueba debería ser e.errno! = 3; quizás e.errno! = errno.ESRCH
Jason R. Coombs
¿Por qué? ESRCH significa "no tal proceso".
Giampaolo Rodolà
2
Correcto. Dado que ESRCH significa "no existe tal proceso", errno! = ESRCH significa "no existe tal proceso" o "proceso existe", que es muy similar al nombre de la función. Ha mencionado específicamente EPERM, pero ¿qué implican los otros posibles códigos de error? Parece incorrecto señalar un código de error que está vagamente relacionado con la intención de la verificación, mientras que ESRCH parece estar estrechamente relacionado.
Jason R. Coombs
Tienes razón. Edité el código que ahora refleja lo que sugirió.
Giampaolo Rodolà
en python3 os.kill () lanza ProcessLookupError
martinkunev
19

Las respuestas que implican el envío de 'señal 0' al proceso funcionarán solo si el proceso en cuestión es propiedad del usuario que ejecuta la prueba . De lo contrario, obtendrá un OSErrordebido a los permisos , incluso si el pid existe en el sistema.

Para evitar esta limitación, puede verificar si /proc/<pid>existe:

import os

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

Esto se aplica solo a los sistemas basados ​​en Linux, obviamente.

Omer Dagan
fuente
incorrecto. PermissionErrorsignifica que pid existe , se obtiene ProcessLookupErrorsi pid no existe.
jfs
El OSErrordebido a la denegación de permiso se puede diferenciar de otros - ya sea a través de mirar a errno o por medio de la captura de los más especializados PermissionError/ ProcessLookupErrorexcepciones que se derivan de OSError. Además, solo obtiene el error de permiso si el proceso existe. Por lo tanto, su ejemplo es solo un método alternativo que funciona en Linux y algunos otros Unices, pero no es más completo que llamar correctamente os.kill(pid, 0).
maxschlepzig
Esta solución no es corss-platform. El OP quiere que esté disponible en Unix y Windows. El /procprocfs solo sale en Linux, ni siquiera en BSD u OSX.
Charles
Este método falla si / proc está montado hidepid = 2, el proceso no se mostrará en la lista si es propiedad de otro usuario.
Perkins
8

En Python 3.3+, puede usar nombres de excepción en lugar de constantes errno. Versión 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
fuente
7

Busque aquí la forma específica de Windows de obtener una lista completa de los procesos en ejecución con sus ID. Seria algo como

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]

A continuación, puede verificar el pid obtenido con esta lista. No tengo idea sobre el costo de rendimiento, por lo que será mejor que verifique esto si va a realizar la verificación de pid con frecuencia.

Para * NIx, simplemente use la solución de mluebke.

Alexander Lebedev
fuente
Esto funcionó bien para mí. Quería buscar un nombre de proceso, así que cambié "ProcessID" por "Nombre" y también lo convertí en una lista de verificación para devolver Verdadero o Falso.
JamesR
6

Sobre la base de ntrrgc, he reforzado la versión de Windows para que verifique el código de salida del proceso y verifique los permisos:

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
avión de velocidad
fuente
En realidad, de acuerdo con msdn.microsoft.com/en-us/library/windows/desktop/… , GetExistCodeProcessrequiere los derechos de acceso PROCESS_QUERY_INFORMATIONy PROCESS_QUERY_LIMITED_INFORMATION.
augustomen
Tenga en cuenta que el código es incorrecto. GetExitCodeProcessrecibe un identificador y un puntero y, en esta muestra, recibe una ExitCodeProcessestructura como segundo parámetro cuando debería ser solo un puntero.
Fabio Zadrozny
Después de OpenProcess, es una buena idea comprobar GetLastError. ¡Un ERROR_ACCESS_DENIED allí significa que el proceso existe! Aquí hay un ejemplo completo que usa esto: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule
4

Combinando la respuesta de Giampaolo Rodolà para POSIX y la mía para Windows , obtuve esto:

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
fuente
La versión de Windows no me funciona en Windows 8.1. Tienes que comprobar GetExitCodeProcessy asegurarte de que incluso tienes acceso.
Avión de velocidad
Usar kernel32.OpenProcessúnicamente no es suficiente. Como se señaló aquí , "si el proceso salió recientemente, es posible que todavía exista un pid para el identificador". Si kernel32.OpenProcessdevuelve un valor distinto de cero, aún necesitamos kernel32.GetExitCodeProcessverificar el código de salida.
Miau
2

En Windows, puede hacerlo de esta manera:

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

En primer lugar, en este código intenta obtener un identificador para el proceso con pid dado. Si el identificador es válido, cierre el identificador para el proceso y devuelva True; de lo contrario, devuelve False. Documentación para OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx

Andrei
fuente
2

Esto funcionará para Linux, por ejemplo, si desea verificar si banshee se está ejecutando ... (banshee es un reproductor de música)

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
fuente
Este método es claramente inferior en comparación con usar os.kill(pid, 0)o mirar /proc/{pid}. En lugar de ejecutar una llamada al sistema, su código bifurca a un hijo, ejecuta un shell en ese hijo, el shell interpreta su mini script de shell superfluo, el shell bifurca a otro hijo que ejecuta pgrep y finalmente pgrep itera /proc. Tu respuesta no responde a la pregunta publicada. El OP pidió un método con un PID. Su método requiere un nombre de proceso.
maxschlepzig
-2

Yo diría que use el PID para cualquier propósito con el que lo obtenga y maneje los errores con elegancia. De lo contrario, es una carrera clásica (el PID puede ser válido cuando verificas que es válido, pero desaparece un instante después)

Damien_The_Unbeliever
fuente
Debería haber sido más específico: estoy verificando la INVALIDEZ. Entonces, básicamente quiero poder ver si un pid NO está en uso.
Evan Fosmark
1
Pero, ¿qué vas a hacer con esa respuesta? El instante después de que hayas adquirido ese conocimiento, algo podría usar ese pid.
Damien_The_Unbeliever
@Damien_The_Unbeliever: está bien si algo lo usa después de obtener ese conocimiento, y entiendo lo que está diciendo sobre la condición de carrera, pero puedo asegurarle que no se aplica a mi situación.
Evan Fosmark