确定PID是否存在的快速方法(Windows)?

时间:2009-02-26 20:19:23

标签: python c winapi pid

我意识到“快”有点主观所以我会用一些背景来解释。我正在研究一个名为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库,旨在避免第三方lib依赖项,如pywin32扩展。我需要一个比当前代码更快的解决方案,它不依赖于pywin32或标准Python发行版中没有的其他模块。

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

更新:显然我早期的基准测试存在缺陷 - 我在C中编写了一些简单的测试应用程序,EnumProcesses一直出现速度较慢而OpenProcess(如果PID有效,则与GetProcessExitCode结合使用但是过程有实际上更快并不慢。

4 个答案:

答案 0 :(得分:8)

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

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

答案 1 :(得分:4)

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

答案 2 :(得分:3)

事实证明我的基准测试显然存在缺陷,因为后来的测试显示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(参见Why does OpenProcess succeed even when I add three to the process ID?)<3之内的PID成功(参见{{3}})

答案 3 :(得分: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更改为函数参数值),直到进程结束。