Come verificare se esiste un processo con un dato pid in Python?


109

C'è un modo per verificare se un pid corrisponde a un processo valido? Ricevo un pid da una fonte diversa da quella os.getpid()e devo controllare se un processo con quel pid non esiste sulla macchina.

Ho bisogno che sia disponibile in Unix e Windows. Sto anche controllando se il PID NON è in uso.


2
Windows è un sistema operativo non standard. Questo tipo di cose NON sono portatili. Sapendo che non puoi avere entrambi, qual è la tua priorità? Scegline uno come priorità e modifica la domanda.
S.Lott

26
@ S.Lott Windows è un sistema operativo non standard Questa è una delle osservazioni più stupide che ho visto su SO ...
Piotr Dobrogost

2
@ Piotr Dobrogost: Potete fornire codice che gestisca unix standard POSIX e Windows non standard POSIX? In tal caso, fornire una risposta che (a) risolva il problema e (b) chiarisca che Windows è in qualche modo conforme allo standard POSIX.
S.Lott

3
@PiotrDobrogost Penso che l'osservazione di S.Lott riguardasse più i dettagli di implementazione e il supporto API che la quota di mercato.
Roy Tinker

3
Windows ha certamente meno in comune con altri sistemi operativi popolari rispetto agli altri. (Chiunque faccia sviluppo web può paragonarlo a un prodotto Microsoft altrettanto famigerato.) Ma in risposta a @ S.Lott: Raramente scrivo codice Python per Windows che non dovrebbe funzionare anche su Linux, OSX, BSD, ecc. onestamente non pensare che "prendersela come priorità" sia un consiglio utile, soprattutto perché Python astrae le differenze di piattaforma quanto più possibile.
Michael Scheper

Risposte:


163

L'invio del segnale 0 a un pid solleverà un'eccezione OSError se il pid non è in esecuzione e non farà nulla altrimenti.

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

3
Funziona di sicuro in Linux e OSX, non posso parlare per Windows. Non uccide il processo nel senso che stai chiedendo, invia il segnale 0, che è fondamentalmente "Stai correndo?".
mluebke

10
Questo sicuramente non funziona su Windows, poiché non esistono segnali simili a UNIX.
Alexander Lebedev

13
Per essere completo, dovresti anche controllare il numero di errore per assicurarti che sia 3 (cattura l'eccezione e controlla il primo argomento). Questo è importante se il processo di destinazione esiste ma non hai il permesso di inviare il segnale (per qualsiasi motivo).
Haridsv


15
os.kill è supportato su Windows, ma os.kill(pid, 0)è lo stesso os.kill(pid, signal.CTRL_C_EVENT)che potrebbe interrompere il processo (o fallire). Ottengo un OSErrordove errno==EINVALquando provo questo su un sottoprocesso.
Jason R. Coombs

76

Dai un'occhiata al psutilmodulo:

psutil (sistema python e utilità di processo) è una libreria multipiattaforma per il recupero di informazioni sui processi in esecuzione e sull'utilizzo del sistema (CPU, memoria, dischi, rete) in Python. [...] Attualmente supporta Linux , Windows , OSX , FreeBSD e Sun Solaris , architetture sia a 32 bit che a 64 bit , con versioni di Python dalla 2.6 alla 3.4 (gli utenti di Python 2.4 e 2.5 possono usare la versione 2.1.3) . PyPy è anche noto per funzionare.

Ha una funzione chiamata pid_exists()che puoi usare per verificare se esiste un processo con il dato pid.

Ecco un esempio:

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)

Per riferimento:



Ho avuto un'istanza in cui pid_exists ha smesso di essere affidabile su Windows. Stava riportando male i pid morti come in uso. Questo è un promemoria per mantenere aggiornato il modulo psutil di tanto in tanto, specialmente quando si eseguono aggiornamenti del sistema operativo.
Damon Brodie,

62

Il codice mluebke non è corretto al 100%; kill () può anche aumentare EPERM (accesso negato) nel qual caso ciò significa ovviamente che esiste un processo. Questo dovrebbe funzionare:

(modificato secondo i commenti di 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

Non puoi farlo su Windows a meno che non usi pywin32, ctypes o un modulo di estensione C. Se ti va bene dipendere da una libreria esterna puoi usare psutil :

>>> import psutil
>>> psutil.pid_exists(2353)
True

haridsv suggerisce che il test dovrebbe essere e.errno! = 3; forse e.errno! = errno.ESRCH
Jason R. Coombs

Perché? ESRCH significa "nessun processo del genere".
Giampaolo Rodolà

2
Destra. Poiché ESRCH significa "nessun processo di questo tipo", errno! = ESRCH significa "nessun processo di questo tipo" o "processo esiste", che è molto simile al nome della funzione. Hai citato specificamente EPERM, ma cosa implicano gli altri possibili codici di errore? Non sembra corretto individuare un codice di errore che è vagamente correlato all'intento del controllo, mentre ESRCH sembra strettamente correlato.
Jason R. Coombs

Hai ragione. Ho modificato il codice che ora riflette ciò che hai suggerito.
Giampaolo Rodolà

in python3 os.kill () lancia ProcessLookupError
martinkunev

19

Le risposte che implicano l'invio di "segnale 0" al processo funzioneranno solo se il processo in questione è di proprietà dell'utente che esegue il test . Altrimenti si otterrà un a OSErrorcausa dei permessi , anche se il pid esiste nel sistema.

Per aggirare questa limitazione puoi verificare se /proc/<pid>esiste:

import os

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

Questo vale solo per i sistemi basati su Linux, ovviamente.


sbagliato. PermissionErrorsignifica che pid esiste , si ottiene ProcessLookupErrorse pid non esiste.
jfs

Il OSErrorpermesso negato può essere differenziato da altri - guardando errno o catturando le eccezioni PermissionError/ più specializzate ProcessLookupErrorche derivano da OSError. Inoltre, viene visualizzato l'errore di autorizzazione solo se il processo esiste. Quindi, il tuo esempio è solo un metodo alternativo che funziona su Linux e alcuni altri Unix ma non è più completo di una chiamata corretta os.kill(pid, 0).
maxschlepzig

Questa soluzione non è corss-platform. L'OP vuole che sia disponibile su Unix e Windows. Il /procprocfs esce solo su Linux, nemmeno su BSD o OSX.
Charles

Questo metodo fallisce se / proc è montato hidepid = 2, il processo non verrà mostrato nell'elenco se è di proprietà di un altro utente.
Perkins

8

In Python 3.3+, potresti usare nomi di eccezioni invece di costanti errno. Versione 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

7

Cerca qui il modo specifico per Windows per ottenere l'elenco completo dei processi in esecuzione con i loro ID. Sarebbe qualcosa di simile

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]

È quindi possibile verificare il pid ottenuto rispetto a questo elenco. Non ho idea del costo delle prestazioni, quindi è meglio che controlli questo se hai intenzione di eseguire spesso la verifica del PID.

Per * NIx, usa semplicemente la soluzione di mluebke.


Questo ha funzionato bene per me. Volevo controllare il nome di un processo, quindi ho sostituito "ProcessID" con "Nome" e l'ho anche convertito in un controllo in elenco per restituire True o False.
JamesR

6

Basandosi su ntrrgc ho potenziato la versione di Windows in modo che controlli il codice di uscita del processo e controlli le autorizzazioni:

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

In realtà, secondo msdn.microsoft.com/en-us/library/windows/desktop/… , GetExistCodeProcessrichiede i diritti di accesso PROCESS_QUERY_INFORMATIONe PROCESS_QUERY_LIMITED_INFORMATION.
augustomen

Nota che il codice è sbagliato. GetExitCodeProcessriceve un handle e un puntatore e in questo esempio riceve una ExitCodeProcessstruttura come secondo parametro quando dovrebbe essere solo un puntatore.
Fabio Zadrozny

Dopo OpenProcess, è una buona idea controllare GetLastError. Un ERROR_ACCESS_DENIED significa che il processo esiste! Ecco un esempio completo che utilizza questo: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule

4

Combinando la risposta di Giampaolo Rodolà per POSIX e la mia per Windows ho ottenuto questo:

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

La versione per Windows non funziona per me su Windows 8.1. Devi controllare GetExitCodeProcesse assicurarti di avere accesso.
velivolo

L'utilizzo da kernel32.OpenProcesssolo non è sufficiente. Come qui sottolineato "se il processo è terminato di recente, potrebbe ancora esistere un pid per l'handle". Se kernel32.OpenProcessrestituisce un valore diverso da zero, è ancora necessario kernel32.GetExitCodeProcesscontrollare ulteriormente il codice di uscita.
Meow

2

In Windows, puoi farlo in questo modo:

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

Prima di tutto, in questo codice provi a ottenere un handle per il processo con pid dato. Se l'handle è valido, chiudere l'handle per il processo e restituire True; in caso contrario, restituisci False. Documentazione per OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx


2

Funzionerà per Linux, ad esempio se vuoi controllare se banshee è in esecuzione ... (banshee è un lettore musicale)

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

Questo metodo è chiaramente inferiore rispetto all'uso os.kill(pid, 0)o allo sguardo /proc/{pid}. Invece di eseguire una chiamata di sistema, il tuo codice esegue il fork di un figlio, esegue una shell in quel figlio, la shell interpreta il tuo superfluo mini script di shell, la shell esegue il fork di un altro figlio che esegue pgrep e infine pgrep esegue l'iterazione /proc. La tua risposta non risponde alla domanda pubblicata. L'OP ha chiesto un metodo dato un PID. Il tuo metodo richiede un nome di processo.
maxschlepzig

-2

Direi che usa il PID per qualsiasi scopo lo stai ottenendo e gestisci gli errori con grazia. Altrimenti, è una gara classica (il PID potrebbe essere valido quando controlli che sia valido, ma sparisci un istante dopo)


Avrei dovuto essere più specifico: sto verificando l'INVALIDITÀ. Quindi, fondamentalmente voglio essere in grado di vedere se un pid NON è in uso.
Evan Fosmark

1
Ma cosa farai con quella risposta? L'istante dopo che hai acquisito quella conoscenza, qualcosa potrebbe usare quel pid.
Damien_The_Unbeliever

@Damien_The_Unbeliever - va bene se qualcosa lo sta usando dopo aver acquisito quella conoscenza, e capisco cosa stai dicendo sulla condizione di gara, ma posso assicurarti che non si applica alla mia situazione.
Evan Fosmark,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.