Быстрый способ определить, существует ли PID (Windows)?

Я понимаю, что "быстрый" немного субъективен, поэтому я объясню с некоторым контекстом. Я работаю над модулем Python под названием psutil для чтения информации о процессе кросс-платформенным способом. Одной из функций является pid_exists(pid) функция для определения, находится ли PID в текущем списке процессов.

прямо сейчас я делаю это очевидным способом, используя EnumProcesses() вытащить список процессов, то interating по списку и ищем ПИД-РЕГУЛЯТОР. Однако некоторые простые бенчмаркинги показывают, что это значительно медленнее, чем функция pid_exists на UNIX-платформах (Linux, OS X, FreeBSD), где мы используем kill(pid, 0) с сигналом 0, чтобы определить, существует ли PID. Дополнительное тестирование показывает, что это EnumProcesses, которые занимают почти все время.

кто-нибудь знает более быстрый способ, чем использование EnumProcesses, чтобы определить, существует ли PID? Я пытался!--13-->OpenProcess () и проверка ошибки открытия несуществующего процесса, но это оказалось более чем в 4 раза медленнее, чем итерация по списку EnumProcesses, так что это тоже. Любые другие (лучшие) предложения?

Примечание: это библиотека Python, предназначенная для предотвращения сторонних зависимостей lib, таких как расширения pywin32. Мне нужно решение, которое быстрее, чем наш текущий код, и которое не зависит от pywin32 или других модулей, отсутствующих в стандартном дистрибутиве Python.

редактировать: чтобы уточнить-мы хорошо осознавая, что существуют расовые условия, присущие процессу чтения iformation. Мы создаем исключения, если процесс уходит во время сбора данных или мы сталкиваемся с другими проблемами. Функция pid_exists () не предназначена для замены правильной обработки ошибок.

обновление: по-видимому, мои предыдущие тесты были ошибочными - я написал несколько простых тестовых приложений на C, и EnumProcesses последовательно выходит медленнее и OpenProcess (в сочетании с GetProcessExitCode в если PID действителен, но процесс остановился) на самом деле много быстрее не медленнее.

4 ответов


OpenProcess может сказать вам без перечисления всех. Понятия не имею, как быстро.

редактировать: обратите внимание, что вам также нужно GetExitCodeProcess чтобы проверить состояние процесса, даже если вы получите ручку от OpenProcess.


при использовании функции pid_exists существует неотъемлемое условие гонки: к тому времени, когда вызывающая программа получит ответ, процесс может уже исчезнуть или может быть создан новый процесс с запрошенным идентификатором. Я бы осмелился сказать, что любое приложение, которое использует эту функцию, имеет недостатки в дизайне, и поэтому оптимизация этой функции не стоит усилий.


оказывается, что мои бенчмарки, очевидно, были как-то испорчены, так как позднее тестирование показывает, что OpenProcess и GetExitCodeProcess намного быстрее, чем использование EnumProcesses. Я не уверен, что произошло, но я сделал несколько новых тестов и проверил, что это более быстрое решение:

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

обратите внимание, что вам нужно использовать GetExitCodeProcess(), потому что OpenProcess() будет успешным для процессов, которые недавно умерли, поэтому вы не можете предположить, что допустимый дескриптор процесса означает, что процесс запущен.

также обратите внимание, что OpenProcess() успешно для PID, которые находятся в пределах 3 от любого допустимого PID (см. почему OpenProcess преуспевает, даже если я добавляю три к идентификатору процесса?)


Я бы закодировал последнюю функцию Джея таким образом.

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

основное различие заключается в закрытии дескриптора процесса (важно, когда клиент этой функции работает в течение длительного времени) и стратегии обнаружения завершения процесса. WaitForSingleObject дает вам возможность подождать некоторое время (изменение 0 на значение параметра функции), пока процесс не закончится.