《编程之美》中的“让CPU占用率曲线听你指挥”一题,作者给出的解法3非常清晰简洁。其思想就是直接查询当前CPU占用率,若过高则Sleep一段时间,否则一直循环。代码使用C#编写。于是自己想将这一思路使用C/C++来实现。
那么首先需要查清楚windows提供了哪些操作性能监视器(perfmon.msc)的API。在网上搜索一下,在vckbase上有一篇文章恰好是讲解这一主题的。这些操作性能监视器的API都以pdh开头。只要知道这一点,就可以在MSDN上查到完整资料。
在MSDN中的索引中输入PDH,列出的第一条主题就是:Platform SDK: Performance Monitoring—Using the PDH Interface.这篇概述文章中描述了PDH可以做什么以及如何使用它们。使用PDH接口操作性能监视器的方法可以概括为以下五个步骤:
1. 创建一个查询(Create a query)。相关的API是PdhOpenQuery。
2. 在已创建的查询中添加一个或多个计数器(Add counters to the query)。相关的API是PdhAddCounter。这个API需要一个描述计数器的字符串参数。MSDN上给出了四种构造符合语法的字符串的方法。其中最容易的方法是使用PdhMakeCounterPath函数。
3. 收集性能数据(collect the performance data)。与此相关的API是PdhCollectQueryData。
4. 处理这此收集到的性能数据(Process the performance data)。与此相关的有数个API。PdhGetFormattedCounterValue这个函数用来获得指定格式的数据。
5. 完成任务后,关闭这个查询(Close the query)。相关的API是PdhCloseQuery。
以上五步中第二步构造描述计数器的字符串有些陌生。它牵扯到一个数据结构,这个数据结构的定义如下:
- typedef struct _PDH_COUNTER_PATH_ELEMENTS {
- LPTSTR szMachineName;
- LPTSTR szObjectName;
- LPTSTR szInstanceName;
- LPTSTR szParentInstance;
- DWORD dwInstanceIndex;
- LPTSTR szCounterName;
- } PDH_COUNTER_PATH_ELEMENTS, *PPDH_COUNTER_PATH_ELEMENTS;
如果不知道如何填充这个数据结构,最好的办法就是打开性能监视器(开始——运行——输入“perfmon.msc”),在图表框中右击,选择添加计数器,在弹出的“添加计数器”对话框中,可以通过下柆列表或列表框选择计算机(数据结构的szMachineName项)、性能对象(szObjectName)、选择计数器(与Object对应的szCounterName)、选择范例(szInstanceName)。可以照着填充。
在我的程序中需要获取CPU使用率。所以选择的对象是Processor,计数器是% Processor Time,范例是_Total。完整的程序代码如下:
- #undef UNICODE
- #undef _UNICODE
- #include <windows.h>
- #include <tchar.h>
- #include <stdio.h>
- #include <pdh.h>
- #pragma comment(lib, "pdh.lib")
- HQUERY hQuery = NULL;
- // 处理ctrl-c异常
- BOOL WINAPI HandlerRoutine(
- DWORD dwCtrlType // control signal type
- ) {
- if(dwCtrlType == CTRL_C_EVENT) {
- printf("ctrl c exception/n");
- if(hQuery) PdhCloseQuery(hQuery);
- }
- return false;
- }
- int main() {
- SetConsoleCtrlHandler(HandlerRoutine, TRUE);
- PDH_STATUS pdhStatus;
- // open query
- pdhStatus = PdhOpenQuery(0, 0, &hQuery);
- if(pdhStatus != ERROR_SUCCESS) {
- printf("PdhOpenQuery failed/n");
- exit(1);
- }
- // construct a counter path
- PDH_COUNTER_PATH_ELEMENTS pcpe;
- TCHAR szFullPathBuffer[MAX_PATH] = TEXT("");
- DWORD dwSize = sizeof(szFullPathBuffer);
- pcpe.szMachineName = TEXT("WANGHAIBIN");
- pcpe.szObjectName = TEXT("Processor");
- pcpe.szInstanceName = TEXT("_Total");
- pcpe.szCounterName = TEXT("% Processor Time");
- pcpe.dwInstanceIndex = -1;
- pcpe.szParentInstance = NULL;
- pdhStatus = PdhMakeCounterPath(&pcpe, szFullPathBuffer, &dwSize, 0);
- if(pdhStatus != ERROR_SUCCESS) {
- printf("PdhMakeCounterPath failed/n");
- goto exit_prog;
- }
- _tprintf(TEXT("path: %s/n"), szFullPathBuffer);
- // add a counter
- HCOUNTER hCounter;
- pdhStatus = PdhAddCounter(hQuery, szFullPathBuffer, 0, &hCounter);
- //pdhStatus = PdhAddCounter(hQuery, TEXT("//Processor(_Total)//% Processor Time"), 0, &hCounter);
- if(pdhStatus != ERROR_SUCCESS) {
- printf("PdhAddCounter failed/n");
- goto exit_prog;
- }
- // collect query data
- pdhStatus = PdhCollectQueryData(hQuery);
- //pdhStatus = PdhCollectQueryDataEx(hQuery, 1, NULL);
- if(pdhStatus != ERROR_SUCCESS) {
- printf("PdhCollectQueryData failed/n");
- goto exit_prog;
- }
- // get counter value
- PDH_FMT_COUNTERVALUE pfc;
- DWORD dwOpt;
- pdhStatus = PdhGetFormattedCounterValue(
- hCounter,PDH_FMT_DOUBLE,&dwOpt,&pfc);
- while(pdhStatus == ERROR_SUCCESS) {
- //printf("%lf/n", pfc.doubleValue);
- pdhStatus = PdhCollectQueryData(hQuery);
- PdhGetFormattedCounterValue(hCounter, PDH_FMT_DOUBLE, &dwOpt, &pfc);
- }
- exit_prog:
- PdhCloseQuery(hQuery);
- return 0;
- }
需要注意的是,上面的% Processor Time中的%和Processor之间有一个空格。不要写错了。
VCKBASE的那篇文章链接:http://www.vckbase.com/document/viewdoc/?id=1434
本文介绍如何使用 C/C++ 和 Windows 的性能监视器 API (PDH) 来监控 CPU 使用率。通过创建查询、添加计数器、收集数据等步骤,实现了一个简单的 CPU 使用率监测程序。
583

被折叠的 条评论
为什么被折叠?



