Perfmon(运行命令perfmon.msc或perfmon即可打开)性能监视器主要用来对指定的系统性能指标进行实时监控。Windows提供Pdh(performance data helper)库,方便通过程序获取里面的性能计数信息。
性能计数器
Windows通过计数器(Counter)提供操作系统、程序、服务以及驱动等的信息,以便对系统进行监视或查找性能瓶颈。
Windows提供了多种方式获取性能计数,下图展示了消费者(获取计数器的程序)、注册表、PDH等之间的互相关联关系:
Pdh接口说明
要使用性能计数器,需要引用对应的库:
#include <Pdh.h>
#include <PdhMsg.h>
#pragma comment(lib,"pdh.lib")
获取计数器一般需要以下步骤:
-
PdhOpenQuery:打开Pdh;
-
PdhAddCounter:添加要关注的计数器(可以添加多个);
-
PdhCollectQueryData:收集计数;因很多计数需要区间值,所以需要调用两次Query(间隔至少1s),然后再获取计数值。
-
PdhGetFormattedCounterValue:获取计数值;
计数器路径
每个计数器由其名称与路径(可在性能监视器中查看到)唯一标识:\\Computer\PerfObject(ParentInstance/ObjectInstance#InstanceIndex)\Counter
其中:
-
Computer
:为要监视机器名或IP地址,对本机可忽略; -
PerfObject
:为要监视的性能对象名称,可以是CPU、内存、硬盘、程序等等(如Processor Information
表示CPU,Network Interface
表示网卡); -
ParentInstance,ObjectInstance,InstanceIndex
:在要监视的对象有多个实例时,用于区分;注意,因#
在此被用于表示实例索引,所以ParentInstance等中若有#
则需要替换为_
;
获取计数
通过调用Pdh接口,即可获取对应计数信息。
计数器
要获取性能计数,需要得知对应的计数器路径;常见计数器路径都比较好获取,网卡需要获取当期使用网卡的接口描述。
以下示例获取CPU利用率、可用内存大小,以及网卡的收发数据量。
void getResourceCounter()
{
HQUERY query;
PDH_STATUS status = PdhOpenQuery(NULL, NULL, &query);
if (status != ERROR_SUCCESS)
cout << "Open Query Error" << endl;
HCOUNTER cpuCounter, memCounter;
HCOUNTER recvCounter, sentCounter;
string strGet = getNetInterface();
wstring strInterface = L"\\Network Interface(" + xugd::clib::XuStr::str2wstr(strGet) + L")\\";
wcout << strInterface << endl;
status = PdhAddCounter(query, TEXT("\\Processor Information(_Total)\\% Processor Time"), NULL, &cpuCounter);
if (status != ERROR_SUCCESS)
cout << "Add CPU Counter Error" << endl;
status = PdhAddCounter(query, TEXT("\\Memory\\Available MBytes"), NULL, &memCounter);
if (status != ERROR_SUCCESS)
cout << "Add Memory Counter Error" << endl;
status = PdhAddCounter(query, (strInterface + L"Bytes Received/sec").c_str(), NULL, &recvCounter);
if (status != ERROR_SUCCESS)
cout << "Add Received Counter Error" << endl;
status = PdhAddCounter(query, (strInterface + L"Bytes Sent/sec").c_str(), NULL, &sentCounter);
if (status != ERROR_SUCCESS)
cout << "Add Sent Counter Error" << endl;
int nIndex = 0;
cout << setiosflags(ios::fixed) << setprecision(4);
while (true) {
PdhCollectQueryData(query);
Sleep(1000);
PdhCollectQueryData(query);
PDH_FMT_COUNTERVALUE pdhValue;
DWORD dwValue;
status = PdhGetFormattedCounterValue(cpuCounter, PDH_FMT_DOUBLE, &dwValue, &pdhValue);
if (status != ERROR_SUCCESS)
cout << "Get Value Error" << endl;
cout << setw(3) << ++nIndex << " - CPU: " << pdhValue.doubleValue << "%";
status = PdhGetFormattedCounterValue(memCounter, PDH_FMT_LONG, &dwValue, &pdhValue);
if (status != ERROR_SUCCESS)
cout << "Get Value Error" << endl;
cout << "; \tMemory: " << pdhValue.longValue << "MB";
status = PdhGetFormattedCounterValue(recvCounter, PDH_FMT_LONG, &dwValue, &pdhValue);
if (status != ERROR_SUCCESS)
cout << "Get Value Error" << endl;
cout << "; \tRecv: " << pdhValue.longValue;
status = PdhGetFormattedCounterValue(sentCounter, PDH_FMT_LONG, &dwValue, &pdhValue);
if (status != ERROR_SUCCESS)
cout << "Get Value Error" << endl;
cout << "; \tSent: " << pdhValue.longValue << endl;
Sleep(1000 * 10);
}
PdhCloseQuery(query);
}
网卡接口
一般电脑上都有多个网卡接口,如何获取当前正在使用的呢?可以通过GetBestInterface接口获取对应索引,然后通过GetAdaptersInfo获取具体信息。
笔记本上等很可能使用的是无线网卡,所以不能根据网卡类型(type)判断。
#include <IPHlpApi.h>
#pragma comment(lib, "IPHlpApi.lib")
string getNetInterface() {
ULONG ulSize = 0;
IP_ADAPTER_INFO *pAdapter = nullptr;
if (GetAdaptersInfo(pAdapter, &ulSize) == ERROR_BUFFER_OVERFLOW) {
pAdapter = (IP_ADAPTER_INFO*)new char[ulSize];
}
else {
cout << "GetAdaptersInfo fail" << endl;
return "";
}
if (GetAdaptersInfo(pAdapter, &ulSize) != ERROR_SUCCESS) {
cout<< "GetAdaptersInfo fail" << endl;
return "";
}
IPAddr ipAddr = { 0 };
DWORD dwIndex = -1;
DWORD nRet = GetBestInterface(ipAddr, &dwIndex);
if (NO_ERROR != nRet) {
cout << "GetBestInterface fail: " << nRet << endl;
}
string strInterface;
for (auto *pCur = pAdapter; pCur != NULL; pCur = pCur->Next) {
//if (pCur->Type != MIB_IF_TYPE_ETHERNET)
// continue;
if (pCur->Index == dwIndex) {
cout << "Best Interface!! ";
strInterface = pCur->Description;
}
cout << "Descrip: " << pCur->Description;
cout << ", Name: " << pCur->AdapterName << endl;
cout << "IP: " << pCur->IpAddressList.IpAddress.String;
cout << ", Gateway: " << pCur->GatewayList.IpAddress.String << endl << endl;
}
delete pAdapter;
return strInterface;
}