C++第四十二篇 -- CPU Usage
阅读原文时间:2023年07月09日阅读:2

目的:读取并控制CPU占用率

近期在做CPU Usage方面的事情,让CPU以一种高占用率的状态运行一定的时间,需要读取CPU各个核的占用率,网上关于这方面的资料好少,FQ也只找到了一个WMI的方法,但是感觉对比任务管理器里面的结果偏小。目前也只能读取CPU总的占用率,和任务管理器看起来差不多,也不是完全相同。

虽然还没有实现结果,但是想记录一下历程。

参考链接:

https://blog.csdn.net/wangting627/article/details/22931337

https://blog.csdn.net/fyxichen/article/details/50577580

1. 任务管理器查看CPU占用率。

方法一:

方法二:

2. C++读取CPU的占用率

原理:先读取一次CPU的当前的空闲时间,内核时间和用户时间,间隔一秒,再次读取三个时间,经过公式演算,得到CPU在这一秒内的占用率。

第一次读取:

FILETIME ftIdle, ftKernel, ftUser;  
BOOL flag = FALSE;  
if (flag = GetSystemTimes(&ftIdle, &ftKernel, &ftUser))  
{  
    m\_fOldCPUIdleTime = FileTimeToDouble(&ftIdle);  
    m\_fOldCPUKernelTime = FileTimeToDouble(&ftKernel);  
    m\_fOldCPUUserTime = FileTimeToDouble(&ftUser);

}  
return flag;

第二次读取:

FILETIME ftIdle, ftKernel, ftUser;  
//检索系统定时信息,对于多处理器系统,返回值是各处理器总和的时间  
//参数1:空闲时间  
//参数2:内核时间  
//参数3:用户时间  
//函数成功,返回值为非零。函数失败,返回值为零  
if (GetSystemTimes(&ftIdle, &ftKernel, &ftUser))  
{  
    double fCPUIdleTime = FileTimeToDouble(&ftIdle);  
    double fCPUKernelTime = FileTimeToDouble(&ftKernel);  
    double fCPUUserTime = FileTimeToDouble(&ftUser);}

计算占用率:

//CPU使用率 = (总的时间-空闲时间) / 总的时间
nCPUUseRate = (int)(100.0 - (fCPUIdleTime - m_fOldCPUIdleTime) / (fCPUKernelTime - m_fOldCPUKernelTime + fCPUUserTime - m_fOldCPUUserTime)*100.0);

3. 控制CPU占用率

原理:CPU的占用率 = (CPU总时间 - CPU空闲时间)/ CPU总时间。

举几个例子看看:

占用率

num值

空闲时间:运行时间

50%

1

1:1

66.7%

2

1:2

80%

4

1:4

90%

9

1:9

如果想要控制CPU的占用率是50%,那么就设置num = 1,空闲时间和运行时间的比例为1:1。不过这些都有个前提是,电脑中并没有运行其他的程序。但是显然不可能,所以这个占用率也只是个大概。并且想要数据比较准确,一秒之内的空闲和运行最好是间隔运行,如果控制前0.5s运行,后0.5秒Sleep,效果也是不好的,在代码中设置的是0.1s休息。

int busytime = 100;  
int idletime = busytime;  
long starttime = 0;  
while (true)  
{  
    starttime = GetTickCount();  
    //std::cout<<starttime<<std::endl;//调试用  
    while ((GetTickCount() - starttime) <= busytime \* num)  
        ;  
    Sleep(idletime);  
}

4. 效果图

以1:9的代码运行结果如下:

可以看到其实任务管理器中的值每隔一秒刷新一次,我写的程序是每隔一秒刷新一次,然后任务管理器中的值看起来比代码中的偏高一些,大胆猜测一下是因为我的程序也在运行,运行过程中也会增加占用率。

5. WMI读取CPU各个核的使用率

代码是google找到的,链接我忘了,后续找到补上,需要FQ

bool getCpuLoadInfo() {
HRESULT hres;

// Step 1: --------------------------------------------------  
// Initialize COM. ------------------------------------------

hres = CoInitializeEx(0, COINIT\_MULTITHREADED);  
if (FAILED(hres)) {  
    cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;  
    return 0;                  // Program has failed.  
}

// Step 2: --------------------------------------------------  
// Set general COM security levels --------------------------  
// Note: If you are using Windows 2000, you need to specify -  
// the default authentication credentials for a user by using  
// a SOLE\_AUTHENTICATION\_LIST structure in the pAuthList ----  
// parameter of CoInitializeSecurity ------------------------

hres = CoInitializeSecurity(  
    NULL,  
    -1,                          // COM authentication  
    NULL,                        // Authentication services  
    NULL,                        // Reserved  
    RPC\_C\_AUTHN\_LEVEL\_DEFAULT,   // Default authentication  
    RPC\_C\_IMP\_LEVEL\_IMPERSONATE, // Default Impersonation  
    NULL,                        // Authentication info  
    EOAC\_NONE,                   // Additional capabilities  
    NULL                         // Reserved  
);

if (FAILED(hres)) {  
    cout << "Failed to initialize security. Error code = 0x"  
        << hex << hres << endl;  
    CoUninitialize();  
    return 0;                    // Program has failed.  
}

// Step 3: ---------------------------------------------------  
// Obtain the initial locator to WMI -------------------------

IWbemLocator \*pLoc = NULL;

hres = CoCreateInstance(  
    CLSID\_WbemLocator,  
    0,  
    CLSCTX\_INPROC\_SERVER,  
    IID\_IWbemLocator, (LPVOID \*)&pLoc);

if (FAILED(hres)) {  
    cout << "Failed to create IWbemLocator object."  
        << " Err code = 0x"  
        << hex << hres << endl;  
    CoUninitialize();  
    return 0;                 // Program has failed.  
}

// Step 4: -----------------------------------------------------  
// Connect to WMI through the IWbemLocator::ConnectServer method

IWbemServices \*pSvc = NULL;

// Connect to the root\\cimv2 namespace with  
// the current user and obtain pointer pSvc  
// to make IWbemServices calls.  
hres = pLoc->ConnectServer(  
    \_bstr\_t(L"ROOT\\\\CIMV2"), // Object path of WMI namespace  
    NULL,                    // User name. NULL = current user  
    NULL,                    // User password. NULL = current  
    0,                       // Locale. NULL indicates current  
    NULL,                    // Security flags.  
    0,                       // Authority (e.g. Kerberos)  
    0,                       // Context object  
    &pSvc                    // pointer to IWbemServices proxy  
);

if (FAILED(hres)) {  
    cout << "Could not connect. Error code = 0x" << hex << hres << endl;  
    pLoc->Release();  
    CoUninitialize();  
    return 0;                // Program has failed.  
}

cout << "Connected to ROOT\\\\CIMV2 WMI namespace" << endl;

// Step 5: --------------------------------------------------  
// Set security levels on the proxy -------------------------

hres = CoSetProxyBlanket(  
    pSvc,                        // Indicates the proxy to set  
    RPC\_C\_AUTHN\_WINNT,           // RPC\_C\_AUTHN\_xxx  
    RPC\_C\_AUTHZ\_NONE,            // RPC\_C\_AUTHZ\_xxx  
    NULL,                        // Server principal name  
    RPC\_C\_AUTHN\_LEVEL\_CALL,      // RPC\_C\_AUTHN\_LEVEL\_xxx  
    RPC\_C\_IMP\_LEVEL\_IMPERSONATE, // RPC\_C\_IMP\_LEVEL\_xxx  
    NULL,                        // client identity  
    EOAC\_NONE                    // proxy capabilities  
);

if (FAILED(hres)) {  
    cout << "Could not set proxy blanket. Error code = 0x"  
        << hex << hres << endl;  
    pSvc->Release();  
    pLoc->Release();  
    CoUninitialize();  
    return 0;               // Program has failed.  
}

// Step 6: --------------------------------------------------  
// Use the IWbemServices pointer to make requests of WMI ----

// For example, get the name of the operating system  
IEnumWbemClassObject\* pEnumerator = NULL;  
IWbemClassObject \*pclsObj;  
int i;  
//while (1) {  
    i = 1;  
    hres = pSvc->ExecQuery(  
        bstr\_t("WQL"),  
        bstr\_t("SELECT \* FROM Win32\_PerfFormattedData\_PerfOS\_Processor"),

        WBEM\_FLAG\_FORWARD\_ONLY | WBEM\_FLAG\_RETURN\_IMMEDIATELY,  
        NULL,  
        &pEnumerator);

    if (FAILED(hres)) {  
        cout << "Query for operating system name failed."  
            << " Error code = 0x"  
            << hex << hres << endl;  
        pSvc->Release();  
        pLoc->Release();  
        CoUninitialize();  
        return 0;               // Program has failed.  
    }

    // Step 7: -------------------------------------------------  
    // Get the data from the query in step 6 -------------------

    ULONG uReturn = 0;

    while (pEnumerator) {  
        HRESULT hr = pEnumerator->Next(WBEM\_INFINITE, 1,  
            &pclsObj, &uReturn);

        if (0 == uReturn) {  
            break;  
        }

        VARIANT vtProp;

        // Get the value of the Name property  
        //hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);  
        hr = pclsObj->Get(L"PercentProcessorTime", 0, &vtProp, 0, 0);  
        wcout << " CPU Usage of CPU " << i << " : " << vtProp.bstrVal << endl;  
        VariantClear(&vtProp);

        //IMPORTANT!!  
        pclsObj->Release();

        i++;  
    }

//}

// Cleanup  
// ========

pSvc->Release();  
pLoc->Release();  
pEnumerator->Release();  
//pclsObj->Release();  
CoUninitialize();

return 0;   // Program successfully completed.  

}

以1:9的比例

先看一下它运行之后的效果图:

看右侧任务管理器中的图形,自始至终没有低于90%,可是WMI读出来的却有70%的出现,况且我自己设置的比例是1:9,自己加的占用率,所以感觉WMI读出来的可能有点不合理,偏低了。看资料有几种计算方法,也不知道哪个正确。

另外分享一下WMI的小技巧。文中代码就是找到“PercentProcessorTime”的值,作为CPU的占用率。一起试着在Windows中手动找一找呗。

a. win+R输入"wbemtest"

问题:

试了多种方法,无法知道哪个结果更加权威,也或者是了解并不深,WMI内部的机制也并不是很清楚,任务管理器里面的计算机制是什么也不清楚,还得继续学习进步。这方面的资料很少,因此学习总结做一下分享。

源码:

目前还没有实现每个核的占用率读取,等后续学习深入补上源码。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章