快速确定 PID 是否存在于 (Windows) 上?

问题描述 投票:0回答:4

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

pid_exists(pid)
函数,用于确定 PID 是否在当前进程列表中。

现在我正在以明显的方式执行此操作,使用 EnumProcesses() 来提取进程列表,然后通过列表进行交互并查找 PID。然而,一些简单的基准测试表明,这比基于 UNIX 的平台(Linux、OS X、FreeBSD)上的 pid_exists 函数慢得多,在这些平台上,我们使用带有 0 信号的

kill(pid, 0)
来确定 PID 是否存在。额外的测试表明 EnumProcesses 几乎一直占用着时间。

有人知道比使用 EnumProcesses 更快的方法来确定 PID 是否存在吗?我尝试了 OpenProcess() 并检查打开不存在进程的错误,但这比迭代 EnumProcesses 列表慢 4 倍以上,所以这也是问题。还有其他(更好的)建议吗?

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

编辑:澄清一下 - 我们很清楚读取过程信息中存在固有的竞争条件。如果该过程在数据收集过程中消失或遇到其他问题,我们会提出异常。 pid_exists() 函数并不是为了取代正确的错误处理。

更新:显然我之前的基准测试是有缺陷的 - 我用 C 和 EnumProcesses 编写了一些简单的测试应用程序,结果始终较慢,而 OpenProcess(与 GetProcessExitCode 结合使用,以防 PID 有效但进程已停止)实际上要快得多 不慢。

python c winapi pid
4个回答
8
投票

OpenProcess 可以告诉您无需枚举所有内容。我不知道有多快。

编辑:请注意,即使您从 GetExitCodeProcess 获得了句柄,您也需要

OpenProcess
 来验证进程的状态。


5
投票
事实证明我的基准测试显然存在某种缺陷,因为后来的测试表明 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()
 会在最近死亡的进程上成功,因此您不能假设有效的进程句柄意味着该进程正在运行。 

另请注意,对于任何有效 PID 的 3 以内的 PID,

OpenProcess()

 都会成功(请参阅
为什么即使我向进程 ID 添加 3,OpenProcess 也会成功?


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


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

© www.soinside.com 2019 - 2024. All rights reserved.