获取多处理器Windows CPU利用率的正确方法

3
我想要实现的是获取每个CPU的利用率(我有4个CPU核心,所以我希望像在resmon.exe中一样能够获取它们所有的利用率)以及它们的总和(就像在TaskManager.exe中一样)。

resmon.exe

我在某些来源上读到,获取处理器利用率的方法是对CPU时间进行数学计算。我尝试使用NtQuerySystemInformation获取必要的数据。

#include <windows.h>
#include <vector>
#include <iostream>
#include <winternl.h>

#pragma comment(lib, "Ntdll.lib")

typedef struct
_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_R {
    LARGE_INTEGER IdleTime;
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER DpcTime;
    LARGE_INTEGER InterruptTime;
    ULONG InterruptCount;
} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_R;

static long long toInteger(LARGE_INTEGER const & integer)
{
#ifdef INT64_MAX // Does the compiler natively support 64-bit integers?
        return integer.QuadPart;
#else
        return (static_cast<long long>(integer.HighPart) << 32) | integer.LowPart;
#endif
}

class CPU
{
public:
    uint64_t prev_idle = 0;
    uint64_t prev_ker = 0;
    uint64_t prev_user = 0;
    uint64_t cur_idle = 0;
    uint64_t cur_ker = 0;
    uint64_t cur_user = 0;

    double get()
    {
        SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_R *a = new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_R[4];
        // 4 is the total of CPU (4 cores)
        NtQuerySystemInformation(SystemProcessorPerformanceInformation, a, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_R) * 4, NULL);

        prev_idle = cur_idle;
        prev_ker = cur_ker;
        prev_user = cur_user;

        cur_idle = 0;
        cur_ker = 0;
        cur_user = 0;

        // 4 is the total of CPU (4 cores)
        // Sum up the SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_R array so I can get the utilization from all of the CPU
        for (int i = 0; i < 4; ++i)
        {
            SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_R b = a[i];
            cur_idle += toInteger(b.IdleTime);
            cur_ker += toInteger(b.KernelTime);
            cur_user += toInteger(b.UserTime);
        }

        std::cout << "Cur idle " << cur_idle << '\n';
        std::cout << "Cur ker " << cur_ker << '\n';
        std::cout << "Cur user " << cur_user << '\n';

        uint64_t delta_idle = cur_idle - prev_idle;
        uint64_t delta_kernel = cur_ker - prev_ker;
        uint64_t delta_user = cur_user - prev_user;
        
        std::cout << "Delta idle " << delta_idle << '\n';
        std::cout << "Delta ker " << delta_kernel << '\n';
        std::cout << "Delta user " << delta_user << '\n';

        uint64_t total_sys = delta_kernel + delta_user;
        uint64_t kernel_total = delta_kernel - delta_idle;

        delete[] a;
        // return (total_sys - delta_idle) * 100.0 / total_sys;
        return (kernel_total + delta_user) * 100.0 / total_sys;
    }
};

int main()
{
    CPU a;
    std::cout << "starting" << '\n';
    while(1)
    {
        std::cout << a.get() << '\n';
        Sleep(1000);
    }
    return 0;
}

为了获取单个CPU的利用率,我不必将所有CPU求和,只需选择一个SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION_R数组元素即可。

我的问题是:

  1. 我做得对吗?因为当我使用任务管理器中的CPU利用率检查我的程序时,结果有些不同。

enter image description here

  1. 除了使用 NtQuerySystemInformation,是否还有更好的方法(因为微软已经提到“NtQuerySystemInformation可能在未来的Windows版本中被更改或取消。应用程序应该使用本主题中列出的替代函数。”)?

1
"NtQuerySystemInformation在Windows的未来版本中可能会被更改或不可用。应用程序应使用本主题中列出的替代函数。" - RbMm
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION:使用GetSystemTimes替代,以检索此信息。 - Drake Wu
@DrakeWu-MSFT,我看到了,但是“在多处理器系统上,返回的值是所有处理器上指定时间之和。”我需要获得每个单独的CPU,正如我所提到的。 - Andra
你尝试过使用 WMI 类 Win32_PerfRawData_PerfOS_Processor原始性能数据类 吗?顺便说一下,我已经测试了 NtQuerySystemInformationGetSystemTimes,它们对我来说返回的值是相同的。 - Drake Wu
@DrakeWu-MSFT,我认为WMI无法让我访问各个CPU。是的,NtQuerySystemInformationGetSystemTimes可以产生相同的结果,但是NtQuerySystemInformation提供了更多的细节,需要自己进行求和。 - Andra
1
查询 Win32_PerfRawData_PerfOS_Processor 将为您提供系统中每个逻辑 CPU 的一个“行”/“条目”。例如,在我的计算机上,我有 12 个逻辑 CPU,并查询此类将给我 12 个结果(分别为 Name 0 到 12)。另外,任务管理器也可以显示单个逻辑 CPU。右键单击图表,从上下文菜单中选择“更改图形类型”。 - Christian.K
1个回答

0

我想要达到同样的目的,基于我的研究,标准的Windows任务管理器是一个不好的总体CPU负载来源(如下图)。似乎在任务管理器中各个CPU核心的负载是可以的,但是整体的CPU负载似乎是错误的。

我使用AIDA64压力测试加载了除一个之外的所有CPU核心,但任务管理器显示100%的CPU负载,而实际上并不是这样。但是,当我将DpcTime和InterruptTime添加到内核时间时,我的类似程序显示了与AIDA相同的CPU负载。


欢迎来到Stack Overflow!不幸的是,这个问题特别要求使用WinAPI调用来查询CPU利用率,所以你的答案并没有解决提问者的问题。 - ChrisB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接