[编程点滴]获取进程的CPU占用率

首先, 感觉是个蛋疼的话题, 不过是做某个软件遇到的.

Windows系统的TaskManager里面其实就有这个功能, 显示一个进程的相关信息, 诸如pid,CPU占用率, 内存, 线程数等.

那么TaskManager是怎么求出某个进程的CPU占用率的呢? 用的NtQuerySystemInformation, NtQueryInformationProcess等吧, 貌似有人Debug过, 看到了这些函数的调用.

不过使用NtQuerySystemInformation这样的函数是有问题的, 1它们不是public的, 2是官方说它们在Vista以后的版本里面可能被修改.

一个可观的替代方案是使用GetSystemTimes和GetProcessTimes:

 1 BOOL WINAPI GetSystemTimes(
 2   _Out_opt_  LPFILETIME lpIdleTime,
 3   _Out_opt_  LPFILETIME lpKernelTime,
 4   _Out_opt_  LPFILETIME lpUserTime
 5 );
 6 
 7 BOOL WINAPI GetProcessTimes(
 8   _In_   HANDLE hProcess,
 9   _Out_  LPFILETIME lpCreationTime,
10   _Out_  LPFILETIME lpExitTime,
11   _Out_  LPFILETIME lpKernelTime,
12   _Out_  LPFILETIME lpUserTime
13 );

GetSystemTimes获得系统(自开机以来)处于Kernel状态下面的CPU时间,以及系统处于User状态下的时间,以及Idle的时间.我们只用Kernel时间和User时间, 不用Idle时间.

相应的, GetProcess也能求出一个进程在上面3中状态下的时间.

下面公式可以求出进程的CPU占用率.

                  CpuPercentageEquation

但是事情并没有就此结束, 因为我发现仅仅做上面的工作的话, 在某些系统(当然还是Windows系列...)下面会不奏效, 这就是很多软件开发者考虑的兼容性的问题.

是权限的问题, OpenProcess调用失败是因为你启动的进程没有能够Debug某个进程的权限.

虽然Admin下面有些权限是有的, 但默认的情况下进程的一些访问权限是没有被置为可用状态(即Enabled)的,所以我们要做的首先是使这些权限可用。

下面3个函数基本可以搞定:OpenProcessToken, LookupPrivilegeValue ,AdjustTokenPrivileges(详情见msdn..)

 1 HANDLE WINAPI OpenProcess(
 2   _In_  DWORD dwDesiredAccess,
 3   _In_  BOOL bInheritHandle,
 4   _In_  DWORD dwProcessId
 5 );
 6 
 7 BOOL WINAPI OpenProcessToken(
 8   _In_   HANDLE ProcessHandle,
 9   _In_   DWORD DesiredAccess,
10   _Out_  PHANDLE TokenHandle
11 );
12 
13 BOOL WINAPI LookupPrivilegeValue(
14   _In_opt_  LPCTSTR lpSystemName,
15   _In_      LPCTSTR lpName,
16   _Out_     PLUID lpLuid
17 );
18 
19 BOOL WINAPI AdjustTokenPrivileges(
20   _In_       HANDLE TokenHandle,
21   _In_       BOOL DisableAllPrivileges,
22   _In_opt_   PTOKEN_PRIVILEGES NewState,
23   _In_       DWORD BufferLength,
24   _Out_opt_  PTOKEN_PRIVILEGES PreviousState,
25   _Out_opt_  PDWORD ReturnLength
26 );

下面是获取进程CPU percent的完整代码:

Source Code
  1 //CpuUsage.h
  2 #ifndef _CPU_USAGE_H_
  3 #define _CPU_USAGE_H_
  4 
  5 #include <windows.h>
  6 
  7 class CpuUsage
  8 {
  9 public:
 10     CpuUsage(DWORD dwProcessID);
 11     ULONGLONG GetUsageEx();
 12     ULONGLONG GetSystemNonIdleTimes();
 13     ULONGLONG GetProcessNonIdleTimes();
 14 private:
 15     ULONGLONG SubtractTimes(const FILETIME& ftA, const FILETIME& ftB);
 16     ULONGLONG AddTimes(const FILETIME& ftA, const FILETIME& ftB);
 17     bool EnoughTimePassed();
 18     inline bool IsFirstRun() const { return (m_dwLastRun == 0); }
 19 
 20     //system total times
 21     FILETIME m_ftPrevSysKernel;
 22     FILETIME m_ftPrevSysUser;
 23 
 24     //process times
 25     FILETIME m_ftPrevProcKernel;
 26     FILETIME m_ftPrevProcUser;
 27 
 28     ULONGLONG m_ullPrevSysNonIdleTime;//这个变量和后面的便利记录上次获取的非idle的系统cpu时间和进程cpu时间.
 29     ULONGLONG m_ullPrevProcNonIdleTime;//这个类只绑定一个进程, 在构造函数里面初始化进来..
 30 
 31     ULONGLONG m_nCpuUsage;
 32     ULONGLONG m_dwLastRun;
 33     DWORD m_dwProcessID;
 34     HANDLE m_hProcess;
 35     volatile LONG m_lRunCount;
 36 };
 37 
 38 #endif
 39 
 40 
 41 //CpuUsage.cpp
 42 
 43 #include <windows.h>
 44 #include "CPUusage.h"
 45 #include <strsafe.h>
 46 //#define USE_DEPRECATED_FUNCS
 47 void ErrorMsg(LPTSTR lpszFunction);
 48 BOOL SetPrivilege(HANDLE hProcess, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege);
 49 
 50 #ifdef USE_DEPRECATED_FUNCS
 51 #define SystemBasicInformation                    0
 52 #define SystemPerformanceInformation            2
 53 #define SystemTimeInformation                    3
 54 #define SystemProcessorPerformanceInformation    8
 55 #define ProcessTimes                            4
 56 
 57 #define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart))
 58 
 59 typedef struct
 60 {
 61     DWORD dwUnknown1;
 62     ULONG uKeMaximumIncrement;
 63     ULONG uPageSize;
 64     ULONG uMmNumberOfPhysicalPages;
 65     ULONG uMmLowestPhysicalPage;
 66     ULONG uMmHighestPhysicalPage;
 67     ULONG uAllocationGranularity;
 68     PVOID pLowestUserAddress;
 69     PVOID pMmHighestUserAddress;
 70     ULONG uKeActiveProcessors;
 71     BYTE bKeNumberProcessors;
 72     BYTE bUnknown2;
 73     WORD wUnknown3;
 74 } SYSTEM_BASIC_INFORMATION;
 75 
 76 typedef struct
 77 {
 78     LARGE_INTEGER liIdleTime;
 79     DWORD dwSpare[312];
 80 } SYSTEM_PERFORMANCE_INFORMATION;
 81 
 82 typedef struct
 83 {
 84     LARGE_INTEGER liKeBootTime;
 85     LARGE_INTEGER liKeSystemTime;
 86     LARGE_INTEGER liExpTimeZoneBias;
 87     ULONG uCurrentTimeZoneId;
 88     DWORD dwReserved;
 89 } SYSTEMTEXTIME_INFORMATION;
 90 
 91 typedef struct
 92         _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
 93 {
 94     LARGE_INTEGER IdleTime;
 95     LARGE_INTEGER KernelTime;
 96     LARGE_INTEGER UserTime;
 97     LARGE_INTEGER Reserved1[2];
 98     ULONG Reserved2;
 99 } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
100 
101 typedef struct _KERNEL_USERTEXTIMES
102 {
103     LARGE_INTEGER CreateTime;
104     LARGE_INTEGER ExitTime;
105     LARGE_INTEGER KernelTime;
106     LARGE_INTEGER UserTime;
107 } KERNEL_USERTEXTIMES, *PKERNEL_USERTEXTIMES;
108 
109 typedef LONG (WINAPI *PROCNTQSI)(UINT, PVOID, ULONG, PULONG);
110 PROCNTQSI NtQuerySystemInformation;
111 
112 typedef LONG (WINAPI *PROCNTQIP)(HANDLE, UINT, PVOID, ULONG, PULONG);
113 PROCNTQIP NtQueryInformationProcess;
114 
115 ULONGLONG CpuUsage::GetSystemNonIdleTimes()
116 {
117     SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
118     SYSTEMTEXTIME_INFORMATION SysTimeInfo;
119     SYSTEM_BASIC_INFORMATION SysBaseInfo;
120     SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SysProcPerfInfo[32];
121     LONG status;
122     NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(GetModuleHandle(TEXT("ntdll")), "NtQuerySystemInformation");
123     if (!NtQuerySystemInformation)
124         return 0;
125     status = NtQuerySystemInformation(SystemBasicInformation, &SysBaseInfo, sizeof(SysBaseInfo), NULL);
126     if (status != NO_ERROR)
127     {
128         MessageBox(TEXT("FailSystemInfo"));
129         return 0;
130     }
131     status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, SysProcPerfInfo, sizeof(SysProcPerfInfo), NULL);
132     if(status != NO_ERROR) return 0;
133     int nProcessors = SysBaseInfo.bKeNumberProcessors; //机器内部CPU的个数
134     ULONGLONG ullSysTotal = 0;
135     for(int i = 0; i < nProcessors; i++)
136     {
137         ullSysTotal += SysProcPerfInfo[i].KernelTime.QuadPart + SysProcPerfInfo[i].UserTime.QuadPart;
138     }
139     return ullSysTotal;
140 }
141 
142 ULONGLONG CpuUsage::GetProcessNonIdleTimes()
143 {
144     KERNEL_USERTEXTIMES KernelUserTimes;
145     ::ZeroMemory(&KernelUserTimes, sizeof(KernelUserTimes));
146     NtQueryInformationProcess = (PROCNTQIP)GetProcAddress(GetModuleHandle(TEXT("ntdll")), "NtQueryInformationProcess");
147     LONG status = NtQueryInformationProcess(m_hProcess, ProcessTimes, &KernelUserTimes, sizeof(KernelUserTimes), NULL);
148     if(status == 0)
149     {
150         ErrorExit(TEXT("GetProcessNonIdleTimes"));
151         return 0;
152     }
153     return KernelUserTimes.KernelTime.QuadPart + KernelUserTimes.UserTime.QuadPart;
154 
155 }
156 
157 #endif
158 
159 CpuUsage::CpuUsage(DWORD dwProcessID)
160     : m_nCpuUsage(0),
161       m_dwLastRun(0),
162       m_lRunCount(0),
163       m_dwProcessID(dwProcessID),
164       m_ullPrevProcNonIdleTime(0),
165       m_ullPrevSysNonIdleTime(0)
166 {
167     HANDLE hProcess = GetCurrentProcess();
168     SetPrivilege(hProcess, SE_DEBUG_NAME, TRUE);
169 
170     m_hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION , TRUE, m_dwProcessID);
171     if(m_hProcess == 0)
172     {
173         ErrorMsg(TEXT("OpenProcess"));
174     }
175     ZeroMemory(&m_ftPrevSysKernel, sizeof(FILETIME));
176     ZeroMemory(&m_ftPrevSysUser, sizeof(FILETIME));
177 
178     ZeroMemory(&m_ftPrevProcKernel, sizeof(FILETIME));
179     ZeroMemory(&m_ftPrevProcUser, sizeof(FILETIME));
180 }
181 
182 
183 ULONGLONG CpuUsage::SubtractTimes(const FILETIME &ftA, const FILETIME &ftB)
184 {
185     LARGE_INTEGER a, b;
186     a.LowPart = ftA.dwLowDateTime;
187     a.HighPart = ftA.dwHighDateTime;
188 
189     b.LowPart = ftB.dwLowDateTime;
190     b.HighPart = ftB.dwHighDateTime;
191 
192     return a.QuadPart - b.QuadPart;
193 }
194 
195 ULONGLONG CpuUsage::AddTimes(const FILETIME &ftA, const FILETIME &ftB)
196 {
197     LARGE_INTEGER a, b;
198     a.LowPart = ftA.dwLowDateTime;
199     a.HighPart = ftA.dwHighDateTime;
200 
201     b.LowPart = ftB.dwLowDateTime;
202     b.HighPart = ftB.dwHighDateTime;
203 
204     return a.QuadPart + b.QuadPart;
205 }
206 
207 bool CpuUsage::EnoughTimePassed()
208 {
209     const int minElapsedMS = 250;//milliseconds
210 
211     ULONGLONG dwCurrentTickCount = GetTickCount();
212     return (dwCurrentTickCount - m_dwLastRun) > minElapsedMS;
213 }
214 #ifndef USE_DEPRECATED_FUNCS
215 
216 ULONGLONG CpuUsage::GetSystemNonIdleTimes()
217 {
218     FILETIME ftSysIdle, ftSysKernel, ftSysUser;
219     if(!GetSystemTimes(&ftSysIdle, &ftSysKernel, &ftSysUser))
220     {
221         ErrorMsg(TEXT("GetSystemTimes"));
222         return 0;
223     }
224     return AddTimes(ftSysKernel, ftSysUser);
225 }
226 
227 
228 ULONGLONG CpuUsage::GetProcessNonIdleTimes()
229 {
230     FILETIME ftProcCreation, ftProcExit, ftProcKernel, ftProcUser;
231     if(!GetProcessTimes(m_hProcess, &ftProcCreation, &ftProcExit, &ftProcKernel, &ftProcUser) && false)
232     {
233         ErrorMsg(TEXT("GetProcessNonIdleTimes"));
234         return 0;
235     }
236     return AddTimes(ftProcKernel, ftProcUser);
237 }
238 #endif
239 
240 ULONGLONG CpuUsage::GetUsageEx()
241 {
242     ULONGLONG nCpuCopy = m_nCpuUsage;
243     if (::InterlockedIncrement(&m_lRunCount) == 1)
244     {
245         if (!EnoughTimePassed())
246         {
247             ::InterlockedDecrement(&m_lRunCount);
248             return nCpuCopy;
249         }
250         ULONGLONG ullSysNonIdleTime = GetSystemNonIdleTimes();
251         ULONGLONG ullProcNonIdleTime = GetProcessNonIdleTimes();
252         if (!IsFirstRun())
253         {
254             ULONGLONG ullTotalSys = ullSysNonIdleTime - m_ullPrevSysNonIdleTime;
255             if(ullTotalSys == 0)
256             {
257                 ::InterlockedDecrement(&m_lRunCount);
258                 return nCpuCopy;
259             }
260             m_nCpuUsage = ULONGLONG((ullProcNonIdleTime - m_ullPrevProcNonIdleTime) * 100.0 / (ullTotalSys));
261             m_ullPrevSysNonIdleTime = ullSysNonIdleTime;
262             m_ullPrevProcNonIdleTime = ullProcNonIdleTime;
263         }
264         m_dwLastRun = (ULONGLONG)GetTickCount();
265         nCpuCopy = m_nCpuUsage;
266     }
267     ::InterlockedDecrement(&m_lRunCount);
268     return nCpuCopy;
269 }
270 
271 BOOL SetPrivilege(HANDLE hProcess, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
272 {
273     HANDLE hToken;
274     if(!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
275     {
276         ErrorMsg(TEXT("OpenProcessToken"));
277         return FALSE;
278     }
279     LUID luid;
280     if(!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
281     {
282         ErrorMsg(TEXT("LookupPrivilegeValue"));
283         return FALSE;
284     }
285     TOKEN_PRIVILEGES tkp;
286     tkp.PrivilegeCount = 1;
287     tkp.Privileges[0].Luid = luid;
288     tkp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : FALSE;
289     if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
290     {
291         ErrorMsg(TEXT("AdjustTokenPrivileges"));
292         return FALSE;
293     }
294     return TRUE;
295 }
296 
297 void ErrorMsg(LPTSTR lpszFunction)
298 {
299     // Retrieve the system error message for the last-error code
300 
301     LPVOID lpMsgBuf;
302     LPVOID lpDisplayBuf;
303     DWORD dw = GetLastError();
304 
305     FormatMessage(
306         FORMAT_MESSAGE_ALLOCATE_BUFFER |
307         FORMAT_MESSAGE_FROM_SYSTEM |
308         FORMAT_MESSAGE_IGNORE_INSERTS,
309         NULL,
310         dw,
311         LANG_USER_DEFAULT,
312         (LPTSTR) &lpMsgBuf,
313         0, NULL );
314 
315     // Display the error message
316 
317     lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
318                                       (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
319     StringCchPrintf((LPTSTR)lpDisplayBuf,
320                     LocalSize(lpDisplayBuf) / sizeof(TCHAR),
321                     TEXT("%s failed with error %d: %s"),
322                     lpszFunction, dw, lpMsgBuf);
323     MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
324 
325     LocalFree(lpMsgBuf);
326     LocalFree(lpDisplayBuf);
327     ExitProcess(dw);
328 }
329 
330 //main.cpp
331 
332 #include "cpuusage.h"
333 #include <fstream>
334 #include <cstdlib>
335 #include <cstdio>
336 using namespace std;
337 
338 const int second = 1000;
339 
340 int main(int argc, char* argv[])
341 {
342     if(argc != 2)
343     {
344         printf("Use the toolkit like: <toolkit name> <pid>\n");
345         return 0;
346     }
347     DWORD dwProcId = atoi(argv[1]);
348     CpuUsage cu(dwProcId);
349     SYSTEMTIME st;
350     while(true)
351     {
352         GetLocalTime(&st);
353         printf("Process(pid:%d) uses %I64d%% cpu at %02d:%02d.%02d\n", dwProcId, cu.GetUsageEx(), st.wHour, st.wMinute, st.wSecond);
354         ::Sleep(second);
355     }
356 }

搞上面那坨代码的时候我发现了ProcessHacker, 它是个开源版本的Process Explorer, 如果你对Windows里面那些乌七八糟的东西感兴趣的话, 可以看看.

ProcessHacker: http://processhacker.sourceforge.net/ 

posted on 2012-11-02 00:12  Jackiesteed 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/jackiesteed/articles/2750540.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值