l Idle状态判断标准
Idle状态判断周期为15分钟,有三个条件:1.在这个周期内没有输入;2.在这个周期内CPU空闲时间超过90%;3.在这个周期内磁盘的空闲时间超过90%。
l Windows下获取CPU使用率的方法大概有三种:
1. Windows自带的API,一般包含在头文件windows.h中;
2. Performance Data Helper(PDH),性能数据助手;
3. Windows ManagementInstrumentation(WMI),windows管理规格。
综合Idle状态不仅要判断CPU空闲时间,还要判断磁盘空闲时间,用PDH库获取更进一步的性能数据来的更简单方便。
根据MSDN中PDH使用的文档,使用pdh库获取性能数据需要执行以下几个步骤:
n 使用pdh库前,需要包含相应的头文件和加载相应的库:
#pragma comment (lib,”pdh.lib”)
#include <Pdh.h>
#include <PdhMsg.h>
1. 创建查询(Create a query)
2. 添加查询到计数器(Add counters to the query)
3. 收集性能数据(Collect the performance data)
4. 关闭查询(Close the query)
核心代码如下:
BOOL Querysystemperformance(LPCTSTRCounterPath)
{
HQUERYquery;
PDH_STATUSstatus = PdhOpenQuery(NULL, NULL, &query);
if(status != ERROR_SUCCESS)
returnFALSE;
HCOUNTERcounter;
counter= (HCOUNTER *)GlobalAlloc(GPTR, sizeof(HCOUNTER));
status= PdhAddCounter(query, CounterPath, NULL, &counter);
if(status != ERROR_SUCCESS)
returnFALSE;
PdhCollectQueryData(query);
Sleep(1000);
PdhCollectQueryData(query);
PDH_FMT_COUNTERVALUEpdhValue;
DWORDdwValue;
status= PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, &dwValue,&pdhValue);
if(status != ERROR_SUCCESS)
returnFALSE;
cout<<pdhValue.doubleValue <<endl;
PdhCloseQuery(query);
returnTRUE;
}
n 在添加查询到计数器时,用到函数PdhAddCounter,函数原型如下:
PDH_STATUS PdhAddCounter( _In
_ PDH_HQUERY hQuery, _In
_ LPCTSTR szFullCounterPath, _In
_ DWORD_PTR dwUserData, _Out
_ PDH_HCOUNTER
*phCounter );
这里第二个参数保湿一个完整的计数器路径(
szFullCounterPath
)
,
计数器路径可以有很多方法表示,本例使用字符串方法表示格式如下:
ComputerperfObject(ParentInstance/objectInstance#InstanceIndex)counter
以下是
MSDN
上对该表示方法的介绍:
The Computer element specifies the name or IPaddress of the computer from which you want to query performance data. Thecomputer name is optional if the counter is located on the local computer.
The PerfObject element specifies theperformance object to query. A performance object can be a physical component,such as processors, disks, and memory, or a system object, such as processesand threads. Each system object is related to a functional element within thecomputer and has a set of standard counters assigned to it. Each computer mayhave a different set of performance objects and counters installed on itbecause applications can install their own performance objects and counters.For a list of the performance objects and counters installed on your computer,see theAdd Counters dialog box in the Performance tool on yourcomputer. These objects are also listed in the PDH browse dialog box (seeBrowsing Counters). For a list of system performance objects and counters, seeCounters by Object.
The ParentInstance, ObjectInstance, andInstanceIndex are included in the path if multiple instances of the object canexist. For example, processes and threads are multiple instance objects becausemore than one process or thread can run at the same time. If an object can havemore than one instance, the counter path must specify an object instance.
在这里我要说的是可以根据系统的Performance Monitor(性能监视器)获取该参数。
1、 打开性能监视器,此时显示的是CPU占用率。
2、 下方计数器处,右击-》属性,就可以看到计数器完整路径。
3、 下图是磁盘空闲时间计数器的完整路径。
如果想要获取其他系统性能数据,在性能监视器汇总加入其他计数器,然后查看属性获取计数器完整路径即可。
n 如果打开性能监视器弹出如下图的警告,可参考
http://help.stnts.com/html/201503/3912.html解决。
创建查询
在使用pdh 库之前,我们需要包含相应的头文件和加载相应的库:
#include <Pdh.h>
...
#pragma comment(lib,"pdh.lib")
创建查询使用函数是PdhOpenQuery,我们看一下官方文档对于这个函数的定义。
PDH_STATUS PdhOpenQuery(
_In_ LPCTSTR szDataSource,
_In_ DWORD_PTR dwUserData,
_Out_ PDH_HQUERY *phQuery
);
- szDataSource
该参数用来指定日志文件的名字以获取性能数据,一般设置成NULL,表示实时获取数据。 - dwUserData
和查询相关的值,不特别指定一般也是NULL。 - phQuery
用于查询的句柄,这个稍后会用到。
好了,接下来就开始查询:
HQUERY query;
PDH_STATUS status = PdhOpenQuery(NULL, NULL, &query);
需要注意的是,如果执行成功,则PdhOpenQuery返回ERROR_SUCCESS,我们可以写个判读语句判读是否执行成功。
if (status != ERROR_SUCCESS)
cout << "Open Query Error" << endl;
添加查询到计数器
查看文档,对应的函数为 PdhAddCounter,先看一下函数原型。
PDH_STATUS PdhAddCounter(
_In_ PDH_HQUERY hQuery,
_In_ LPCTSTR szFullCounterPath,
_In_ DWORD_PTR dwUserData,
_Out_ PDH_HCOUNTER *phCounter
);
使用该函数我们可以添加query到计数器中,注意第二个参数,这表示还需要一个完整的计数器路径(szFullCounterPath),这个计数器路径怎么写,文档中也给出了几种方法,我们这里介绍字符串方法。
这种方法直接指定计数器路径为一个字符串,如果总是监视同一个计数器,并且你对计数器路径的的格式和语法比较熟悉,那用这个方法比较合适。
计数器路径的格式如下:
\\Computer\PerfObject(ParentInstance/ObjectInstance#InstanceIndex)\Counter
看了一头雾水是不是?参照官方文档我们一点一点解释。
-
Computer
这个指目标机器的名字或者ip地址,如果是获取本机数据,则可以省略。 -
PerfObject
这个指的是要查询的性能对象,这个性能对象可以是硬件(比如处理器,磁盘,内存),也可以是系统对象(比如进程,线程) -
ParentInstance/ObjectInstance#InstanceIndex
如果查询的对象存在多实例,路径中就包含这三者。
举个例子,进程和线程就是多实例对象,因为同一时间能运行多个进程或线程。
那么如果一个对象有多实例,计数器路径就必须指定到某一个对象实例。
说的再详细点,假设我们要监控Explorer,那格式就是:
(Explorer)
如果要监控该进程下的某个子线程,那格式就是:
(Explorer/0)
如果该进程下的子线程名字都一样,则通过#符号来区分开,需要注意的是,子线程的索引从0开始,但是查询第一个子线程不要写#0,直接写线程名字,从第二个子线程开始才是#1,那么第三个子线程就是#2,以此类推。
(Explorer/0#1)
- Counter
最后就是这个Counter,这就是你想要查询的计数器。
最终这个格式是什么样子的呢,我们这里要查询的是CPU 的使用率,那就得知道某一刻CPU总的使用时间,格式如下:
\Processor Information(_Total)\% Processor Time
注意% 和Processor 之间有个空格,对于反斜杠’\’,我们要写两次用来转义,实际代码如下。
HCOUNTER counter;
counter = (HCOUNTER *)GlobalAlloc(GPTR, sizeof(HCOUNTER));
status = PdhAddCounter(query, LPCSTR("\\Processor Information(_Total)\\% Processor Time"), NULL, &counter);
if (status != ERROR_SUCCESS)
{
//...
}
那对于cpu使用率是这种格式,那其他性能计数器是什么格式呢?凭空捏造可造不出来!
其实,对于windows系统,我们打开性能监视器就可以看到,运行窗口输入”perfmon.msc”,如图:
点击确定就可以看到本机系统的性能监视器:
在性能监视器页面,当前显示的是”% Processor Time”,也就是cpu的使用时间,那假如要看内存应该怎么操作?
首先,图表页面右击,选择添加计数器
然后显示如图页面,从下面的列表中找到Memory,然后选择Available MBytes,这表示可用内存(单位:MB),然后点击添加,这样我们就添加了可用内存的计数器。
然后,刚才的监视器页面就多了一条计数器,看到没?
但这还只是图形化的计数器,我们要的是具体的计数器路径呀?
别急,在对应的”Available MBytes”上右击—>属性,就能看到对应的计数器路径了,怎么样,是不是很方便?
知道对应的计数器路径,我们在代码中就可以使用了。
收集性能数据
将query 添加到计数器之后,我们就要开始收集性能数据了,相应函数:
PDH_STATUS PdhCollectQueryData(
_Inout_ PDH_HQUERY hQuery
);
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
需要注意的是,大部分像使用率这样的计数器,需要收集两份样本,中间用Sleep()函数间隔1s 或者更久。
PdhCollectQueryData(query);
Sleep(1000);
PdhCollectQueryData(query);
显示性能数据
收集完数据之后,就要将数据显示出来了,函数如下:
PDH_STATUS PdhGetFormattedCounterValue(
_In_ PDH_HCOUNTER hCounter,
_In_ DWORD dwFormat,
_Out_ LPDWORD lpdwType,
_Out_ PPDH_FMT_COUNTERVALUE pValue
);
各个参数可以查阅官方文档,我们这里获取CPU的使用率。PDH_FMT_COUNTERVALUE pdhValue;
DWORD dwValue;
status = PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &dwValue, &pdhValue);
if (status != ERROR_SUCCESS)
{
//...
}
对于第二个参数,一般情况下我们使用PDH_FMT_DOUBLE,但是对于计数对象是处理器,我们还需要考虑要不要PDH_FMT_NOCAP100。
如果设置本标记,则计数器的百分比数值的上限可以大于100,例如:多个处理器的使用率。
如果不设置本标记,则计数器的百分比数值的上限为100。
现在,我们可以获取cpu 的使用率了。
cout << pdhValue.doubleValue << endl;
关闭查询
既然打开了查询,那最后就要关闭查询。
PdhCloseQuery(query);
最后照例,放上程序源码(windows8.1 + visual studio2013)。
#pragma comment(lib,"pdh.lib")
#include <iostream>
#include <Pdh.h>
#include <PdhMsg.h>
using namespace std;
int main(int argc, char **argv)
{
HQUERY query;
PDH_STATUS status = PdhOpenQuery(NULL, NULL, &query);
if (status != ERROR_SUCCESS)
cout << "Open Query Error" << endl;
HCOUNTER counter;
counter = (HCOUNTER *)GlobalAlloc(GPTR, sizeof(HCOUNTER));
status = PdhAddCounter(query, LPCSTR("\\Processor Information(_Total)\\% Processor Time"), NULL, &counter);
if (status != ERROR_SUCCESS)
cout << "Add Counter Error" << endl;
PdhCollectQueryData(query);
Sleep(1000);
PdhCollectQueryData(query);
PDH_FMT_COUNTERVALUE pdhValue;
DWORD dwValue;
status = PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, &dwValue, &pdhValue);
if (status != ERROR_SUCCESS)
cout << "Get Value Error" << endl;
cout << pdhValue.doubleValue << endl;
PdhCloseQuery(query);
return 0;