windows下cpu和性能指标监控Top10的实现

一直以来只在linux下用过好用的top命令,可以显示出排名最前的应用cpu和内存占用情况。windows下虽然有进程管理器,性能监视器perfmon.msc,resmon资源监视器,processhacker神器等众多性能指标监控工具可以用,但就是没法看top10那样的直观。

Process Hacker介绍

Process Hacker:windows下的一款用于调试和排除软件故障的强大工具 。它可以帮助研究人员检测和解决软件或进程在特定操作系统环境下遇到的问题。除此之外,它还可以检测恶意进程,并告知我们这些恶意进程想要实现的功能。

Process Hacker, A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware.

Top10的实现

可以用来做一款监控报警服务方便定制,对系统和应用的运行情况进行监控,记录异常报警日志方便运维和问题分析。

先来看张效果图:

 实现原理

使用windows计数器和windows性能监视器pdh库。pdh库可以很简单的获取windows下CPU占用率、网络占用率、内存占用率、网络上下行速度等。

PDH库的使用

#ifndef UNICODE
#define UNICODE
#endif // !UNICODE
#include <pdh.h>
#include <pdhmsg.h>
#include <tchar.h>
#include <windows.h>

//! 链接使用pdh库
#pragma	comment(lib,"pdh")

简单的五步:
1、打开pdh查询 PdhOpenQuery

HQUERY query;

PDH_STATUS status = PdhOpenQuery(NULL,NULL,&query);

if( status !=ERROR_SUCCESS )

2、添加计数器

HCOUNTER cpuCounter;

status = PdhAddCounter(query,TEXT("\\Processor Information(_Total)\\% Processor Time"),NULL,&cpuCounter);

3、收集性能数据

PdhCollectQueryData(query);

Sleep(1000); // 此处时间可更换,但必须有延时至少1秒,否则结果不准确

PdhCollectQueryData(query);

4、获取统计的结果值

PDH_FMT_COUNTERVALUE pdhValue;

DWORD dwValue;

PDH_STATUS status =PdhGetFormattedCounterValue(cpuCounter,PDH_FMT_DOUBLE,&dwValue,&pdhValue);
if(status == ERROR_SUCCESS)

5、移除计数器

PdhRemoveCounter(cpuCounter);

6、关闭pdh查询

PdhCloseQuery(query);

Top10的具体实现思路

通过编写程序来访问Windows性能计数器。获取所有进程的cpu占用率和内存使用信息,然后存入map排序。

Windows中的注册表是访问性能计数器的一种机制。性能信息并不实际存在于注册表中,在注册表编辑器RegEdit.exe中是无法查看的,但可以通过注册表函数来访问,利用注册表键来获得从性能数据提供者那里提供的数据。打开名为HKEY_PERFORMANCE_DATA的特殊键,利用RegQueryValueEx函数查询键下面的值,就可以直接访问注册表性能计数器信息。

注册表中计时器的位置是在:

"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"

获取后存储进map结构,按cpu和内存占用大小排序。需要清楚数据在注册表中的存储格式。

HKEY_PERFORMANCE_DATA数据组织

性能数据的头部是一个PERF_DATA_BLOCK结构(如图1所示),它描述系统和性能数据总体信息,可从Global键值处查询得到该结构数据。PERF_DATA_BLOCK之后,定义了系统中的全部性能对象类型(PERF_OBJECT_TYPE),其中每个对象类型头部中描述了下一个性能对象类型的偏移量Offset。

图一

性能对象有两种:一种是单实例对象,另一种是多实例对象。

图2和图3分别描述了这两种性能对象的数据组织方式。每个对象数据块包含了一个PERF_OBJECT_TYPE结构,描述对象的性能数据。紧随其后是PERF_COUNTER_DEFINITION结构列表,描述了性能对象的全部计数器定义。对于单实例对象,计数器定义列表后是一个PERF_COUNTER_BLOCK结构,计数器数据紧随其后。

每个PERF_COUNTER_DEFINITION结构中定义了计数器数据相对于PERF_COUNTER_BLOCK的偏移量,因此可以非常方便地获得全部计数器的值。对支持多实例性能对象来说,PERF_COUNTER_DEFINITION结构列表之后是一组实例信息数据块,每个表示代表一个对象实例。每个实例信息数据块由一个PERF_INSTANCE_DEFINITION结构体、实例名和一个PERF_COUNTER_BLOCK结构体组成。后面是计数器值数据,与单实例对象相同。

图二 

图三

 以下为相关代码实现:

//根据[Process]的ID,获得与进程有关的数据块
	TCHAR szTempProcessId[256] = {0};
	wsprintf(szTempProcessId, L"%d", nProcess009Id);
	DWORD dwSize = 204800;
	PPERF_DATA_BLOCK pPerfDataBlock = NULL;
	pPerfDataBlock = (PPERF_DATA_BLOCK)malloc(dwSize);
	if (pPerfDataBlock == NULL){
		RegCloseKey(hPerfKey);
		sprintf((char*)strErrorInfo.c_str(), "分配内存失败");
		printf("malloc Memory to pPerfDataBlock Error, Id: %d\n", GetLastError());
		return FALSE;
	}
	while (true){
		LResult = RegQueryValueEx(hPerfKey, (szTempProcessId), NULL, NULL, (LPBYTE)pPerfDataBlock, &dwSize);
		if (LResult == ERROR_SUCCESS){
			break;
		}
		else if (LResult == ERROR_MORE_DATA){
			dwSize += 102400;
			pPerfDataBlock = (PPERF_DATA_BLOCK)realloc((void*)pPerfDataBlock, dwSize);
			if (pPerfDataBlock == NULL){
				RegCloseKey(hPerfKey);
				sprintf((char*)strErrorInfo.c_str(), "分配内存失败");
				printf("realloc Memory Error, Id: %d\n", GetLastError());
				return FALSE;
			}
		}else{
			RegCloseKey(hPerfKey);
			free((void*)pPerfDataBlock);
			sprintf((char*)strErrorInfo.c_str(), "未知错误");
			printf("RegQueryValueEx [Process] Error, Id: %d\n", GetLastError());
			return FALSE;
		}
	}
// 获取全部进程的cpuMem信息,按内存占用从大到小排序
BOOL CCpuTop::GetCpuMemMap()
{
	LONGLONG tm = tm2 - tm1;
	int i = 1;
	for (itTempMap = mapCpuTemp.begin(); itTempMap != mapCpuTemp.end(); itTempMap++) {
		if ((itTempMap->second.strProcessName != "Idle") && (itTempMap->second.strProcessName != "_Total")) {
			// cpu使用率
			double lfUsage = itTempMap->second.FirstData * 100.0 / tm;
			// 内存占用,单位字节
			unsigned __int64 memSize = itTempMap->second.SecondData;
			int nCpuNumber = GetCpuNumber();
			// 除以cpu核数才是对的
			lfUsage = lfUsage / nCpuNumber;

			CCpuInfo aCpuInfo(itTempMap->first, itTempMap->second.strProcessName, lfUsage, memSize);
			aInfoNode = std::make_pair(aCpuInfo, "");
			// 插入数据,自动按规则排序
			InfoCheckPair = mapCpuMem.insert(aInfoNode);
			if (!InfoCheckPair.second) {
				sprintf((char*)strErrorInfo.c_str(), "获取数据失败");
				printf("第%d次: Insert a node to mapCpuInfo Error\n", i);
				return FALSE;
			}
			i++;
		}
	}

	return TRUE;
}
class CCpuTop
{
public:
	CCpuTop(void);
	~CCpuTop(void);

public:
	std::map<int, CCpuTemp> mapCpuTemp;
	std::map<int, CCpuTemp>::iterator itTempMap;

	std::pair<int, CCpuTemp> aTempNode;
	std::pair<std::map<int, CCpuTemp>::iterator, bool> TempCheckPair;
	// 带排序规则的map cpu占用率排序
	std::map<CCpuInfo, std::string, CCpuCompare> mapCpuInfo;
	// 带排序规则的map 内存占用大小排序
	std::map<CCpuInfo, std::string, CCpuMemCompare> mapCpuMem;

	std::pair<CCpuInfo, std::string> aInfoNode;

	std::pair<std::map<CCpuInfo, std::string, CCpuCompare>::iterator, bool> InfoCheckPair;
//......
}
// cpu信息 进程id,进程名,使用率,内存占用
class CCpuInfo
{
public:
	CCpuInfo(void);
	CCpuInfo(int _nProcessId, std::string _strProcessName, double _fValue, unsigned __int64 _memSize);
	~CCpuInfo(void);

public:
	int nProcessId;
	// 进程名字
	std::string strProcessName;
	// cpu占用率
	double fValue;
	// 内存使用
	unsigned __int64 memSize;
};

// 排序规则定义类 cpu占用率大小比较
class CCpuCompare
{
public:
	CCpuCompare(void) = default;
	~CCpuCompare(void) = default;

public:
	bool operator()(const CCpuInfo& aCpuInfo, const CCpuInfo& bCpuInfo) const; 
};

// 排序规则定义类 内存占用大小比较
class CCpuMemCompare
{
public:
	CCpuMemCompare(void) = default;
	~CCpuMemCompare(void) = default;

public:
	bool operator()(const CCpuInfo& aCpuInfo, const CCpuInfo& bCpuInfo) const;
};

参考文献


[1] 深入解析Windows操作系统(第四版), Mark E.Russinovich & David A.Solomon.
[2] Using Performance Counters, MSDN 开发人员工具、技术文档和代码示例 | Microsoft Docs
[3] 纵谈进程枚举, 陆其明. http://jasonye.bokee.com/432054.html.
[4] 如何编程获取Windows NT的性能数据, 周京生. http://www.comprg.com.cn/detail.asp?hw_id=2643.

引用

Process Hacker 简单介绍_一杯咖啡的时间的博客-CSDN博客_processhacker使用

Overview - Process Hacker

Process Hacker:一款用于调试和排除软件故障的强大工具 - FreeBuf网络安全行业门户

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
实现C语言监控WindowsCPU的Top 10进程,可以考虑以下步骤: 1. 使用Windows API的函数获取当前进程列表,包括每个进程的PID和CPU占用率等信息。 2. 将获取到的每个进程的CPU占用率进行排序,找出占用率最高的Top 10进程。 3. 将Top 10进程的PID和CPU占用率等信息记录下来,可以输出到控制台、日志文件或者发送到其他应用程序进行处理。 下面是一个简单的C语言代码示例,可以实现监控WindowsCPU的Top 10进程: ``` #include <windows.h> #include <stdio.h> #define MAX_PROCESSES 1024 typedef struct { DWORD pid; float cpu_usage; } PROCESS_INFO; int compare_process_info(const void *a, const void *b) { const PROCESS_INFO *pa = (const PROCESS_INFO *)a; const PROCESS_INFO *pb = (const PROCESS_INFO *)b; if (pa->cpu_usage > pb->cpu_usage) { return -1; } else if (pa->cpu_usage < pb->cpu_usage) { return 1; } else { return 0; } } int main() { PROCESS_INFO processes[MAX_PROCESSES]; DWORD num_processes = 0; float cpu_total = 0.0f; while (1) { // 获取进程列表 DWORD cbNeeded; DWORD processIds[MAX_PROCESSES]; if (!EnumProcesses(processIds, sizeof(processIds), &cbNeeded)) { printf("Error: EnumProcesses failed!\n"); break; } num_processes = cbNeeded / sizeof(DWORD); // 计算每个进程的CPU占用率 cpu_total = 0.0f; for (DWORD i = 0; i < num_processes; ++i) { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIds[i]); if (hProcess) { FILETIME dummy, creationTime, exitTime, kernelTime, userTime; if (GetProcessTimes(hProcess, &dummy, &dummy, &kernelTime, &userTime) && GetSystemTimeAsFileTime(&dummy) && GetProcessTimes(hProcess, &creationTime, &exitTime, &dummy, &dummy)) { ULARGE_INTEGER kernelTimeLarge, userTimeLarge; kernelTimeLarge.LowPart = kernelTime.dwLowDateTime; kernelTimeLarge.HighPart = kernelTime.dwHighDateTime; userTimeLarge.LowPart = userTime.dwLowDateTime; userTimeLarge.HighPart = userTime.dwHighDateTime; float cpu_usage = (kernelTimeLarge.QuadPart + userTimeLarge.QuadPart) / (10000.0f * GetTickCount()); processes[i].pid = processIds[i]; processes[i].cpu_usage = cpu_usage; cpu_total += cpu_usage; } CloseHandle(hProcess); } } // 排序并输出Top 10进程 qsort(processes, num_processes, sizeof(PROCESS_INFO), compare_process_info); printf("Top 10 processes:\n"); for (DWORD i = 0; i < 10 && i < num_processes; ++i) { if (processes[i].cpu_usage > 0.0f) { printf("PID: %d, CPU usage: %.2f%%\n", processes[i].pid, processes[i].cpu_usage * 100); } } printf("Total CPU usage: %.2f%%\n", cpu_total * 100); Sleep(1000); } return 0; } ``` 该代码使用了`EnumProcesses`和`GetProcessTimes`等Windows API函数,可以获取进程列表和每个进程的CPU占用率等信息,然后使用`qsort`函数对进程列表进行排序,输出Top 10进程和总CPU占用率。该代码可以作为一个Windows服务运行,不断监控系统中的进程情况,并输出Top 10进程的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

特立独行的猫a

您的鼓励是我的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值