8

我意识到“快速”有点主观,所以我会用一些上下文来解释。我正在开发一个名为psutil的 Python 模块,用于以跨平台方式读取进程信息。其中一个函数是pid_exists(pid)用于确定PID是否在当前进程列表中的函数。

现在我正在以明显的方式执行此操作,使用EnumProcesses()拉取进程列表,然后遍历列表并查找 PID。但是,一些简单的基准测试表明,这比基于 UNIX 的平台(Linux、OS X、FreeBSD)上的 pid_exists 函数要慢得多,我们使用kill(pid, 0)0 信号来确定 PID 是否存在。额外的测试表明,几乎所有时间都在占用 EnumProcesses。

任何人都知道比使用 EnumProcesses 确定 PID 是否存在更快的方法吗?我尝试了OpenProcess()并检查打开不存在的进程是否有错误,但结果证明这比遍历 EnumProcesses 列表慢 4 倍以上,所以这也是如此。还有其他(更好的)建议吗?

注意:这是一个 Python 库,旨在避免第三方库依赖项,如 pywin32 扩展。我需要一个比我们当前代码更快的解决方案,并且不依赖于 pywin32 或标准 Python 发行版中不存在的其他模块。

编辑:澄清 - 我们很清楚阅读过程信息中存在固有的竞争条件。如果在数据收集过程中流程消失或遇到其他问题,我们会引发异常。pid_exists() 函数并非旨在替换正确的错误处理。

更新:显然我早期的基准测试存在缺陷——我用 C 语言编写了一些简单的测试应用程序,并且 EnumProcesses 始终运行较慢,而 OpenProcess(与 GetProcessExitCode 结合,以防 PID 有效但进程已停止)实际上更快而不是更慢。

4

4 回答 4

8

OpenProcess可以告诉你没有枚举所有。我不知道有多快。

编辑:请注意,GetExitCodeProcess即使您从中获得句柄,您也需要验证进程的状态OpenProcess

于 2009-02-26T22:41:23.947 回答
4

事实证明,我的基准测试显然存在某种缺陷,因为后来的测试显示 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 都成功(请参阅为什么即使我将三个添加到进程 ID,OpenProcess 也会成功?

于 2009-03-01T18:18:22.277 回答
3

使用 pid_exists 函数有一个固有的竞争条件:当调用程序开始使用答案时,进程可能已经消失,或者可能已经创建了具有查询 id 的新进程。我敢说,任何使用此功能的应用程序都存在设计缺陷,因此优化此功能是不值得的。

于 2009-02-26T21:08:02.940 回答
3

我会以这种方式编写 Jay 的最后一个函数。

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 更改为函数参数值)直到过程结束。

于 2009-03-04T02:13:18.093 回答