确定(Windows)上是否存在PID的快速方法?
我意识到“快”是有点主观,所以我会解释一些上下文。我正在研究一个名为psutil的Python模块,以跨平台的方式读取过程信息。其中一个功能是用于确定PID是否在当前进程列表中的pid_exists(pid)
函数。确定(Windows)上是否存在PID的快速方法?
现在我正在做这个显而易见的方式,使用EnumProcesses()来拉取进程列表,然后遍历列表并查找PID。但是,一些简单的基准测试显示,这比在基于UNIX的平台(Linux,OS X,FreeBSD)上的pid_exists函数慢得多,我们使用kill(pid, 0)
和0信号来确定PID是否存在。额外的测试显示它几乎总是占用EnumProcesses。
任何人都知道比使用EnumProcesses来确定PID是否存在更快的方法吗?我尝试OpenProcess()并检查打开不存在进程的错误,但是这比通过EnumProcesses列表迭代要慢4倍以上,所以也是如此。任何其他(更好)的建议?
注意:这是一个Python库,旨在避免pywin32扩展等第三方lib依赖项。我需要的解决方案比我们当前的代码更快,并且不依赖pywin32或其他标准Python发行版中没有的模块。
编辑:为了澄清 - 我们很清楚,阅读过程中存在固有的竞争条件。如果在数据收集过程中流程消失或遇到其他问题,我们会引发异常。 pid_exists()函数不是用来代替正确的错误处理。
UPDATE:显然,我先前的基准是有缺陷的 - 我写了一些C简单的测试应用程序和EnumProcesses一贯出来慢,OpenProcess(在的情况下与GetProcessExitCode结合的PID是有效的,但该进程已停止)实际上是很快更快不慢。
使用pid_exists函数存在固有的竞争条件:在调用程序获得使用答案时,进程可能已经消失,或者可能已经创建了具有查询ID的新进程。我敢说,使用这个函数的任何应用程序都有缺陷,因此优化这个函数是不值得的。
是的,在任何ps-like应用程序中都存在固有的竞争条件,包括我们的库。但是,这个函数仍然有有效的用例。请注意,如果数据收集过程中的任何时候失败,我们都会*抛出异常,因为该过程已消失。 – Jay 2009-02-26 21:29:10
原来,我的基准测试显然存在缺陷,因为后来的测试显示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(见Why does OpenProcess succeed even when I add three to the process ID?)
谢谢你最后一个注意事项,我正在桌子上敲我的头,以便为什么一个完全不存在的PID返回true。 – 2010-10-16 05:51:13
我的代码周杰伦的最后一个函数这样。
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更改为函数参数值),直到过程结束。
我们不希望在这种情况下等待(其他函数调用将检测进程是否自己关闭并引发Python异常)。但是,关于关闭进程句柄是正确的......我们的“真实”代码关闭了句柄,但是我忘了在我发布的示例中这样做。 – Jay 2009-03-04 16:15:00
尽管我之前测试过,但这是最好的办法。如果感兴趣,请查看我的答案。 – Jay 2009-03-01 18:23:49