Быстрый способ определить, существует ли PID (Windows)? - PullRequest
8 голосов
/ 26 февраля 2009

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

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

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

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

РЕДАКТИРОВАТЬ : Чтобы уточнить - мы хорошо понимаем, что существуют гоночные условия, присущие информации процесса чтения. Мы выдвигаем исключения, если процесс прекращается во время сбора данных или у нас возникают другие проблемы. Функция pid_exists () не предназначена для замены правильной обработки ошибок.

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

Ответы [ 4 ]

8 голосов
/ 27 февраля 2009

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

РЕДАКТИРОВАТЬ : обратите внимание, что вам также нужно GetExitCodeProcess, чтобы проверить состояние процесса, даже если вы получите дескриптор от OpenProcess.

4 голосов
/ 27 февраля 2009

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

3 голосов
/ 04 марта 2009

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

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 на значение параметра функции), пока процесс не завершится.

3 голосов
/ 01 марта 2009

Оказывается, что мои тесты явно были ошибочными, так как последующее тестирование показало, что 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 успешно выполняется, даже когда я добавляю три к идентификатору процесса? )

...