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

StackOverflow https://stackoverflow.com/questions/592256

  •  09-09-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top