2009-02-26 11 views
8

Me doy cuenta de que "rápido" es un poco subjetivo, así que lo explicaré con cierto contexto. Estoy trabajando en un módulo de Python llamado psutil para leer la información del proceso de forma multiplataforma. Una de las funciones es una función pid_exists(pid) para determinar si un PID se encuentra en la lista de procesos actual.¿Forma rápida de determinar si existe un PID en (Windows)?

Ahora estoy haciendo esto de la manera obvia, usando EnumProcesses() para extraer la lista de procesos, luego interar a través de la lista y buscar el PID. Sin embargo, algunos benchmarking simples muestran que esto es dramáticamente más lento que la función pid_exists en plataformas basadas en UNIX (Linux, OS X, FreeBSD) donde estamos usando kill(pid, 0) con una señal 0 para determinar si existe un PID. Pruebas adicionales muestran que EnumProcesses está tomando casi todo el tiempo.

¿Alguien sabe una manera más rápida que usar EnumProcesses para determinar si existe un PID? Intenté OpenProcess() y comprobé un error al abrir el proceso inexistente, pero resultó ser 4 veces más lento que iterar a través de la lista EnumProcesses, por lo que también está fuera. ¿Alguna otra (mejor) sugerencia?

NOTA: Esta es una biblioteca de Python destinada a evitar dependencias lib de terceros como las extensiones pywin32. Necesito una solución que sea más rápida que nuestro código actual, y que no dependa de pywin32 u otros módulos que no estén presentes en una distribución estándar de Python.

EDIT: Para aclarar, somos conscientes de que existen condiciones de carrera inherentes a la información del proceso de lectura. Planteamos excepciones si el proceso desaparece durante el proceso de recopilación de datos o si nos topamos con otros problemas. La función pid_exists() no está pensada para reemplazar el manejo correcto de errores.

ACTUALIZACIÓN: Al parecer mis puntos de referencia anteriores eran defectuosos - Me escribió algunas aplicaciones simples de prueba en C y EnumProcesses constantemente sale más lento y OpenProcess (en conjunción con GetProcessExitCode en caso de que el PID es válido, pero el proceso se ha detenido) es en realidad mucho más rápido no más lento.

Respuesta

8

OpenProcess podría decirte sin enumerar todo. No tengo idea de qué tan rápido.

EDITAR: tenga en cuenta que también necesita GetExitCodeProcess para verificar el estado del proceso, incluso si se obtiene un mango de OpenProcess.

+0

Resulta que a pesar de mis pruebas anteriores esta es la mejor manera de hacerlo después de todo. Ver mi respuesta para más detalles si está interesado. – Jay

4

Existe una condición de carrera inherente en el uso de la función pid_exists: cuando el programa llamante llega a utilizar la respuesta, es posible que el proceso ya haya desaparecido o que se haya creado un nuevo proceso con el ID consultado. Me atrevería a decir que cualquier aplicación que utilice esta función tiene defectos de diseño y que optimizar esta función no vale la pena.

+0

Sí, hay una condición de carrera inherente en cualquier aplicación tipo ps, incluida nuestra biblioteca. Sin embargo, esta función todavía tiene casos de uso válidos. Tenga en cuenta que estamos * también * generando excepciones si en algún momento durante el proceso de recopilación de datos falla porque el proceso ha desaparecido. – Jay

3

Resulta que mis puntos de referencia evidentemente fueron defectuosos de alguna manera, ya que las pruebas posteriores revelan que OpenProcess y GetExitCodeProcess son mucho más rápidos que usar EnumProcesses después de todo. No estoy seguro de lo que pasó, pero hice algunas nuevas pruebas y verificado esta es la solución más rápida:

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

Tenga en cuenta que es necesario utilizar GetExitCodeProcess() porque OpenProcess() tendrá éxito en los procesos que han muerto recientemente, por lo que puede' Supongamos que un identificador de proceso válido significa que el proceso se está ejecutando.

También tenga en cuenta que OpenProcess() tiene éxito para los PID que están a menos de 3 de cualquier ID de producto válido (Ver Why does OpenProcess succeed even when I add three to the process ID?)

+0

Gracias por esa última nota, me estaba golpeando la cabeza en el escritorio sobre por qué un PID completamente inexistente volvía a ser cierto. –

3

Me último código de la función de Jay de esta manera.

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 principal diferencia está cerrando el identificador de proceso (importante cuando el cliente de esta función se está ejecutando desde hace mucho tiempo) y la estrategia de detección de la terminación del proceso. WaitForSingleObject te da la oportunidad de esperar un rato (cambiando el 0 a un valor de parámetro de función) hasta que el proceso finalice.

+0

No deseamos esperar en este caso (otras llamadas a función detectarán si el proceso se ha cerrado y emitirá una excepción a Python). Pero tiene razón al cerrar los identificadores de proceso ... nuestro código "real" cierra los identificadores, pero olvidé hacerlo en la muestra que publiqué. – Jay

Cuestiones relacionadas