2009-02-26 25 views
8

Mi rendo conto che "veloce" è un po 'soggettivo quindi spiegherò con un certo contesto. Sto lavorando su un modulo Python chiamato psutil per leggere le informazioni sul processo in modo multipiattaforma. Una delle funzioni è una funzione pid_exists(pid) per determinare se un PID si trova nell'elenco dei processi corrente.Un modo rapido per determinare se esiste una PID (Windows)?

In questo momento sto facendo questo in modo ovvio, utilizzando EnumProcesses() per estrarre l'elenco dei processi, quindi interating nell'elenco e cercando il PID. Tuttavia, alcuni semplici benchmark mostrano che questo è notevolmente più lento della funzione pid_exists su piattaforme basate su UNIX (Linux, OS X, FreeBSD) dove stiamo usando kill(pid, 0) con un segnale 0 per determinare se esiste un PID. Test aggiuntivi dimostrano che è EnumProcesses che occupa quasi tutto il tempo.

Qualcuno conosce un modo più veloce rispetto all'utilizzo di EnumProcesses per determinare se esiste una PID? Ho provato OpenProcess() e verificato un errore durante l'apertura del processo inesistente, ma si è rivelato essere oltre 4 volte più lento di iterare attraverso l'elenco EnumProcesses, quindi è anche fuori. Qualche altro (migliore) suggerimento?

NOTA: Questa è una libreria Python pensata per evitare dipendenze lib di terze parti come le estensioni pywin32. Ho bisogno di una soluzione che sia più veloce del nostro codice attuale e che non dipenda da pywin32 o altri moduli non presenti in una distribuzione standard di Python.

EDIT: Per chiarire, siamo ben consapevoli del fatto che ci sono condizioni di competizione inerenti alla iformation del processo di lettura. Solleviamo eccezioni se il processo sparisce durante la raccolta dei dati o ci imbattiamo in altri problemi. La funzione pid_exists() non è destinata a sostituire la corretta gestione degli errori.

UPDATE: A quanto pare i miei punti di riferimento precedenti sono state viziate - ho scritto alcune semplici applicazioni di test in C e EnumProcesses costantemente esce più lento e OpenProcess (in collaborazione con GetProcessExitCode nel caso in cui il PID è valido, ma il processo si è fermato) è in realtà molto più veloce non più lento.

risposta

8

OpenProcess potrebbe dirti w/o enumerare tutto. Non ho idea di quanto velocemente.

EDIT: noti che è necessario anche GetExitCodeProcess per verificare lo stato del processo, anche se si ottiene un handle da OpenProcess.

+0

Si scopre che nonostante i miei precedenti test questo è il modo migliore di andare dopo tutto. Vedi la mia risposta per i dettagli se interessati. – Jay

4

C'è una condizione di competizione intrinseca nell'uso della funzione pid_exists: nel momento in cui il programma chiamante utilizza la risposta, il processo potrebbe essere già scomparso oppure è possibile che sia stato creato un nuovo processo con l'ID interrogato. Oserei dire che qualsiasi applicazione che utilizza questa funzione è difettosa in base alla progettazione e quindi l'ottimizzazione di questa funzione non vale la pena.

+0

Sì, c'è una condizione di razza intrinseca in qualsiasi applicazione di tipo ps, inclusa la nostra libreria. Tuttavia, questa funzione ha ancora casi d'uso validi. Si noti che * aumentiamo * le eccezioni anche se in qualsiasi momento durante il processo di raccolta dei dati non riesce perché il processo è scomparso. – Jay

3

Si scopre che i miei benchmark evidentemente erano imperfetti in qualche modo, poiché test successivi rivelano che OpenProcess e GetExitCodeProcess sono molto più veloci dell'utilizzo di EnumProcesses. Non sono sicuro di quello che è successo, ma ho fatto alcuni nuovi test e verificato questa è la soluzione più veloce:

int pid_is_running(DWORD pid) 
{ 
    HANDLE hProcess; 
    DWORD exitCode; 

    //Special case for PID 0 System Idle Process 
    if (pid == 0) { 
     return 1; 
    } 

    //skip testing bogus PIDs 
    if (pid < 0) { 
     return 0; 
    } 

    hProcess = handle_from_pid(pid); 
    if (NULL == hProcess) { 
     //invalid parameter means PID isn't in the system 
     if (GetLastError() == ERROR_INVALID_PARAMETER) { 
      return 0; 
     } 

     //some other error with OpenProcess 
     return -1; 
    } 

    if (GetExitCodeProcess(hProcess, &exitCode)) { 
     CloseHandle(hProcess); 
     return (exitCode == STILL_ACTIVE); 
    } 

    //error in GetExitCodeProcess() 
    CloseHandle(hProcess); 
    return -1; 
} 

noti che si ha bisogno di utilizzare GetExitCodeProcess() perché OpenProcess() riuscirà sui processi che sono morti di recente in modo da poter' Supponiamo che un handle di processo valido significhi che il processo è in esecuzione.

Si noti inoltre che OpenProcess() riesce per PID che sono nel raggio di 3 di qualsiasi PID valido (Vedi Why does OpenProcess succeed even when I add three to the process ID?)

+0

Grazie per quell'ultima nota, stavo sbattendo la testa sulla scrivania per spiegare perché un PID completamente inesistente stava tornando vero. –

3

avrei ultimo codice funzione di Jay in questo modo.

int pid_is_running(DWORD pid){ 
    HANDLE hProcess; 
    DWORD exitCode; 
    //Special case for PID 0 System Idle Process 
    if (pid == 0) { 
     return 1; 
    } 
    //skip testing bogus PIDs 
    if (pid < 0) { 
     return 0; 
    } 
    hProcess = handle_from_pid(pid); 
    if (NULL == hProcess) { 
     //invalid parameter means PID isn't in the system 
     if (GetLastError() == ERROR_INVALID_PARAMETER) { 
      return 0; 
     } 
     //some other error with OpenProcess 
     return -1; 
    } 
    DWORD dwRetval = WaitForSingleObject(hProcess, 0); 
    CloseHandle(hProcess); // otherwise you'll be losing handles 

    switch(dwRetval) { 
    case WAIT_OBJECT_0; 
     return 0; 
    case WAIT_TIMEOUT; 
     return 1; 
    default: 
     return -1; 
    } 
} 

La differenza principale sta chiudendo l'handle di processo (importante quando il client di questa funzione è in esecuzione da molto tempo) e la strategia di rilevamento di terminazione processo. WaitForSingleObject ti dà l'opportunità di aspettare un po '(cambiando lo 0 in un valore del parametro della funzione) finché il processo non termina.

+0

In questo caso non vogliamo aspettare (altre chiamate di funzione rileveranno se il processo si è chiuso e generano un'eccezione in Python). Ma hai ragione a chiudere le maniglie del processo ... il nostro codice "reale" chiude le maniglie ma ho dimenticato di farlo nell'esempio che ho postato. – Jay

Problemi correlati