2009-02-26 5 views
8

Ich realisiere "schnell" ist ein bisschen subjektiv, also werde ich mit etwas Kontext erklären. Ich arbeite an einem Python-Modul namens psutil zum Lesen von Prozessinformationen auf einer plattformübergreifenden Art und Weise. Eine der Funktionen ist eine pid_exists(pid)-Funktion zum Bestimmen, ob eine PID in der aktuellen Prozessliste enthalten ist.Schnelle Möglichkeit zu bestimmen, ob eine PID (Windows) existiert?

Im Moment mache ich dies die offensichtliche Art und Weise, EnumProcesses() zu verwenden, um die Prozessliste zu ziehen, dann durch die Liste zu interagieren und nach der PID zu suchen. Einige einfache Benchmarks zeigen jedoch, dass dies auf UNIX-basierten Plattformen (Linux, OS X, FreeBSD) wesentlich langsamer ist als die pid_exists-Funktion, bei der wir kill(pid, 0) mit einem 0-Signal verwenden, um festzustellen, ob eine PID existiert. Zusätzliche Tests zeigen, dass EnumProcesses fast die ganze Zeit in Anspruch nimmt.

Wer weiß einen schnelleren Weg als mit EnumProcesses zu bestimmen, ob eine PID existiert? Ich versuchte OpenProcess() und prüfte auf einen Fehler, der den nicht vorhandenen Prozess öffnete, aber dieses stellte heraus, um über 4x langsamer zu sein als das Durchlaufen der EnumProcesses Liste, also ist es auch heraus. Irgendwelche anderen (besseren) Vorschläge?

HINWEIS: Dies ist eine Python-Bibliothek, die lib-Abhängigkeiten von Drittanbietern wie pywin32-Erweiterungen vermeiden soll. Ich brauche eine Lösung, die schneller ist als unser aktueller Code, und das hängt nicht von pywin32 oder anderen Modulen ab, die in einer Standard-Python-Distribution nicht vorhanden sind.

BEARBEITEN: Um zu verdeutlichen - wir sind uns bewusst, dass es bei der Leseprozessanalyse Race Conditions gibt. Wir erheben Ausnahmen, wenn der Prozess im Laufe der Datenerfassung aufhört oder wir auf andere Probleme stoßen. Die Funktion pid_exists() soll die ordnungsgemäße Fehlerbehandlung nicht ersetzen.

UPDATE: Offenbar war mein früherer Benchmarks fehlerhaft - eigentlich schrieb ich ein paar einfachen Test Apps in C und EnumProcesses kommt konsequent aus langsamer und Open (in Verbindung mit GetProcessExitCode falls die PID gültig ist, aber der Prozess gestoppt wird) ist viel schneller nicht langsamer.

Antwort

8

OpenProcess könnte Ihnen sagen, ohne alle aufzuzählen. Ich habe keine Ahnung wie schnell.

EDIT: beachten Sie, dass Sie benötigen GetExitCodeProcess auch den Zustand des selbst Prozess zu überprüfen, ob Sie einen Griff aus OpenProcess bekommen.

+0

Stellt sich heraus, trotz meiner früheren Tests ist dies der bessere Weg, um schließlich zu gehen. Siehe meine Antwort für Details, wenn Sie interessiert sind. – Jay

4

Es gibt eine inhärente Race-Bedingung in der Verwendung der Funktion pid_exists: Zu dem Zeitpunkt, an dem das aufrufende Programm die Antwort verwenden soll, ist der Prozess möglicherweise bereits verschwunden oder ein neuer Prozess mit der abgefragten ID wurde erstellt. Ich würde sagen, dass jede Anwendung, die diese Funktion verwendet, vom Design her fehlerhaft ist und dass sich die Optimierung dieser Funktion daher nicht lohnt.

+0

Ja, es gibt eine inhärente Wettlaufsituation in jeder ps-ähnlichen Anwendung, einschließlich unserer Bibliothek. Diese Funktion hat jedoch noch gültige Anwendungsfälle. Beachten Sie, dass wir * auch * Ausnahmen auslösen, wenn zu irgendeinem Zeitpunkt während des Datensammlungsvorgangs ein Fehler auftritt, weil der Prozess verschwunden ist. – Jay

3

Es stellte sich heraus, dass meine Benchmarks offensichtlich fehlerhaft waren, da spätere Tests ergeben, dass OpenProcess und GetExitCodeProcess viel schneller sind als EnumProcesses. Ich bin nicht sicher, was passiert ist, aber ich habe einige neue Tests und überprüft dies ist die schnellere Lösung:

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; 
} 

Beachten Sie, dass GetExitCodeProcess() weil OpenProcess() verwenden, müssen auf Prozesse gelingen, die vor kurzem gestorben sind, so können Sie‘ t Angenommen, eine gültige Prozesskennung bedeutet, dass der Prozess ausgeführt wird.

Beachten Sie auch, dass OpenProcess() für PIDs gelingt, die innerhalb von 3 jeden gültigen PID (siehe Why does OpenProcess succeed even when I add three to the process ID?)

+0

Danke für diese letzte Notiz, ich schlug meinen Kopf auf den Schreibtisch, warum eine komplett nicht existente PID wieder wahr wurde. –

3

ich Jays diese Art und Weise letzte Funktion kodieren würde.

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; 
    } 
} 

Der Hauptunterschied ist das Schließen des Prozess Griff (wichtig, wenn der Client dieser Funktion für eine lange Zeit ausgeführt wird) und die Prozessbeendigung Erkennungsstrategie. WaitForSingleObject gibt Ihnen die Möglichkeit, bis zum Ende des Prozesses eine Weile zu warten (indem Sie den Wert für den Funktionsparameter 0 ändern).

+0

Wir wollen in diesem Fall nicht warten (andere Funktionsaufrufe werden feststellen, ob der Prozess sich selbst geschlossen hat und eine Ausnahme für Python auslöst). Aber Sie haben Recht damit, die Prozess-Handles zu schließen ... unser "echter" Code schließt die Handles, aber ich habe vergessen, dies in der Probe zu tun, die ich gepostet habe. – Jay

Verwandte Themen