确定(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是有效的,但该进程已停止)实际上是很快更快不慢。

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

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

+0

尽管我之前测试过,但这是最好的办法。如果感兴趣,请查看我的答案。 – Jay 2009-03-01 18:23:49

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

+0

是的,在任何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?

+0

谢谢你最后一个注意事项,我正在桌子上敲我的头,以便为什么一个完全不存在的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更改为函数参数值),直到过程结束。

+0

我们不希望在这种情况下等待(其他函数调用将检测进程是否自己关闭并引发Python异常)。但是,关于关闭进程句柄是正确的......我们的“真实”代码关闭了句柄,但是我忘了在我发布的示例中这样做。 – Jay 2009-03-04 16:15:00