C++第四十二篇 -- CPU Usage
前言
目的:读取并控制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内部的机制也并不是很清楚,任务管理器里面的计算机制是什么也不清楚,还得继续学习进步。这方面的资料很少,因此学习总结做一下分享。
源码:
目前还没有实现每个核的占用率读取,等后续学习深入补上源码。