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

发布于 2024-07-14 09:57:10 字数 1225 浏览 4 评论 0 原文

我意识到“快”有点主观,所以我会用一些上下文来解释。 我正在开发一个名为 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 有效但进程已停止)是实际上更快而不是更慢。

I realize "fast" is a bit subjective so I'll explain with some context. I'm working on a Python module called psutil for reading process information in a cross-platform way. One of the functions is a pid_exists(pid) function for determining if a PID is in the current process list.

Right now I'm doing this the obvious way, using EnumProcesses() to pull the process list, then interating through the list and looking for the PID. However, some simple benchmarking shows this is dramatically slower than the pid_exists function on UNIX-based platforms (Linux, OS X, FreeBSD) where we're using kill(pid, 0) with a 0 signal to determine if a PID exists. Additional testing shows it's EnumProcesses that's taking up almost all the time.

Anyone know a faster way than using EnumProcesses to determine if a PID exists? I tried OpenProcess() and checking for an error opening the nonexistent process, but this turned out to be over 4x slower than iterating through the EnumProcesses list, so that's out as well. Any other (better) suggestions?

NOTE: This is a Python library intended to avoid third-party lib dependencies like pywin32 extensions. I need a solution that is faster than our current code, and that doesn't depend on pywin32 or other modules not present in a standard Python distribution.

EDIT: To clarify - we're well aware that there are race conditions inherent in reading process iformation. We raise exceptions if the process goes away during the course of data collection or we run into other problems. The pid_exists() function isn't intended to replace proper error handling.

UPDATE: Apparently my earlier benchmarks were flawed - I wrote some simple test apps in C and EnumProcesses consistently comes out slower and OpenProcess (in conjunction with GetProcessExitCode in case the PID is valid but the process has stopped) is actually much faster not slower.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

于我来说 2024-07-21 09:57:10

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

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

OpenProcess could tell you w/o enumerating all. I have no idea how fast.

EDIT: note that you also need GetExitCodeProcess to verify the state of the process even if you get a handle from OpenProcess.

画中仙 2024-07-21 09:57: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() 将在最近死亡的进程上成功,因此您不能假设有效的进程句柄意味着该进程正在运行。

另请注意,对于任何有效 PID 的 3 以内的 PID,OpenProcess() 会成功(请参阅 为什么即使我在进程 ID 中添加 3,OpenProcess 也会成功?

Turns out that my benchmarks evidently were flawed somehow, as later testing reveals OpenProcess and GetExitCodeProcess are much faster than using EnumProcesses after all. I'm not sure what happened but I did some new tests and verified this is the faster solution:

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;
}

Note that you do need to use GetExitCodeProcess() because OpenProcess() will succeed on processes that have died recently so you can't assume a valid process handle means the process is running.

Also note that OpenProcess() succeeds for PIDs that are within 3 of any valid PID (See Why does OpenProcess succeed even when I add three to the process ID?)

一个人的旅程 2024-07-21 09:57:10

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

There is an inherent race condition in the use of pid_exists function: by the time the calling program gets to use the answer, the process may have already disappeared, or a new process with the queried id may have been created. I would dare say that any application that uses this function is flawed by design and that optimizing this function is therefore not worth the effort.

飘然心甜 2024-07-21 09:57:10

我会用这种方式编写 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 更改为函数参数值),直到进程结束。

I'd code Jay's last function this way.

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;
    }
}

The main difference is closing the process handle (important when the client of this function is running for a long time) and the process termination detection strategy. WaitForSingleObject gives you the opportunity to wait for a while (changing the 0 to a function parameter value) until the process ends.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文