win7之後的系統的CPU佔用計算的原理與實現
阿新 • • 發佈:2019-01-26
經過比對,發現procexp和工作管理員在計算程序cpu佔用上面存在很大的差異,經過研究發現,procexp顯示的是正確的,而工作管理員顯示的是錯誤的,工作管理員是用以前老的方式計算的。
新的cpu計算原理應該是:
程序CPU佔用率 = 程序消耗CPU時間 / 所有程序消耗CPU總時間 * 100%
CycleTime:週期時間(即從程序啟動開始到當前所消耗的CPU時間總和)
所有程序消耗CPU總時間 = 所有程序的週期時間 + 核心時間(DPC以及中斷服務)
Idle程序消耗的CPU時間 = 每個核消耗的CPU時間相加
以下是具體實現:
#include <conio.h> #include <atlbase.h> #include <iostream> #include "SysProcess.h" #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #define NT_INFORMATION(Status) ((((ULONG)(Status)) >> 30) == 1) #define NT_WARNING(Status) ((((ULONG)(Status)) >> 30) == 2) #define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3) #define SYSTEM_IDLE_PROCESS_ID ((HANDLE)0) #define FIND_FIRST_PROCESS(Processes) ((PSYSTEM_PROCESS_INFORMATION)(Processes)) #define FIND_NEXT_PROCESS(Process) ( \ ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \ (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : \ NULL \ ) #define PhUpdateDelta(DltMgr, NewValue) \ ((DltMgr)->Delta = (NewValue) - (DltMgr)->Value, \ (DltMgr)->Value = (NewValue), (DltMgr)->Delta) typedef NTSTATUS (NTAPI *NTQUERYSYSTEMINFORMATION)(IN SYSTEM_INFORMATION_CLASS, IN OUT PVOID, IN ULONG, OUT PULONG OPTIONAL); typedef NTSTATUS (NTAPI *NTQUERYINFORMATIONPROCESS)(IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL); NTQUERYSYSTEMINFORMATION NtQuerySystemInformation = NULL; NTQUERYINFORMATIONPROCESS NtQueryInformationProcess = NULL; ULONG g_ulCpuNumber = 0; std::vector<PROCESS_ITEM_INFORMATION> g_vecSysProInfos; std::map<HANDLE, HANDLE> g_mapProInfos; //key - UniqueProcessId PH_UINT64_DELTA g_phCpuCycleDelta = {0}; //查詢程序是否已存在上一次儲存的程序列表裡面 //vecProInfos上一次儲存的程序列表 //pProcess當前需要查詢的程序 inline BOOL GetProcessInfo(const std::vector<PROCESS_ITEM_INFORMATION> &vecProInfos, ULONG64 ProcessId, ULONG64 CreateTime, PROCESS_ITEM_INFORMATION &itemProcess) { BOOL bRet = FALSE; for (std::vector<PROCESS_ITEM_INFORMATION>::const_iterator iter = vecProInfos.begin(); iter != vecProInfos.end(); ++ iter) { PROCESS_ITEM_INFORMATION item = *iter; if (ProcessId == item.ProcessId && CreateTime == item.CreateTime) { itemProcess = item; bRet = TRUE; } } return bRet; } inline HANDLE GetProcessHandle(HANDLE hUniqueProcessId) { return g_mapProInfos[hUniqueProcessId]; } //獲取CPU核數 inline ULONG GetCpuNumberOfProcessors() { NTSTATUS status; ULONG ulRet = g_ulCpuNumber; PSYSTEM_BASIC_INFORMATION pSysBasicInfo = NULL; do { if (g_ulCpuNumber != 0) { break; } if (NULL == NtQuerySystemInformation) { NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation"); if (NULL == NtQuerySystemInformation) { break; } } ULONG ulSize = sizeof(SYSTEM_BASIC_INFORMATION); pSysBasicInfo = (PSYSTEM_BASIC_INFORMATION)malloc(ulSize); if (NULL == pSysBasicInfo) { break; } status = NtQuerySystemInformation(SystemBasicInformation, pSysBasicInfo, ulSize, NULL); if (!NT_SUCCESS(status)) { break; } ulRet = pSysBasicInfo->NumberOfProcessors; } while (0); if (NULL != pSysBasicInfo) { free(pSysBasicInfo); } return ulRet; } //獲取Idle程序的CPU使用時間 //https://msdn.microsoft.com/en-us/library/windows/desktop/ms684922(v=vs.85).aspx //Retrieves the cycle time for the idle thread of each processor in the system. //On a system with more than 64 processors, this function retrieves the cycle time for the //idle thread of each processor in the processor group to which the calling thread is assigned. //Use the QueryIdleProcessorCycleTimeEx function to retrieve the cycle time for the //idle thread on each logical processor for a specific processor group. inline ULONG64 GetCpuIdleCycleTime() { NTSTATUS status; ULONGLONG ullRet = 0; PLARGE_INTEGER pCpuIdleCycleTime = NULL; do { if (NULL == NtQuerySystemInformation) { NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation"); if (NULL == NtQuerySystemInformation) { break; } } ULONG ulNumberOfProcessors = GetCpuNumberOfProcessors(); ULONG ulSize = ulNumberOfProcessors * sizeof(LARGE_INTEGER); pCpuIdleCycleTime = (PLARGE_INTEGER)malloc(ulSize); if (NULL == pCpuIdleCycleTime) { break; } status = NtQuerySystemInformation(SystemProcessorIdleCycleTimeInformation, pCpuIdleCycleTime, ulSize, NULL); if (!NT_SUCCESS(status)) { break; } for (ULONG i = 0; i < ulNumberOfProcessors; i++) { ullRet += pCpuIdleCycleTime[i].QuadPart; } } while (0); if (NULL != pCpuIdleCycleTime) { free(pCpuIdleCycleTime); } return ullRet; } //https://msdn.microsoft.com/en-us/library/windows/desktop/dd405497(v=vs.85).aspx //Retrieves the cycle time each processor in the specified processor group spent executing deferred procedure calls (DPCs) and interrupt service routines (ISRs) since the processor became active. //檢索指定組中的每個處理器週期推遲執行過程呼叫(dpc)和中斷服務程式 inline ULONG64 GetCpuSystemCycleTime() { NTSTATUS status; ULONGLONG ullRet = 0; ULONGLONG ullTotal = 0; PLARGE_INTEGER pSysCycleTime = NULL; do { if (NULL == NtQuerySystemInformation) { NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation"); if (NULL == NtQuerySystemInformation) { break; } } ULONG ulNumberOfProcessors = GetCpuNumberOfProcessors(); ULONG ulSize = ulNumberOfProcessors * sizeof(LARGE_INTEGER); pSysCycleTime = (PLARGE_INTEGER)malloc(ulSize); if (NULL == pSysCycleTime) { break; } status = NtQuerySystemInformation(SystemProcessorCycleTimeInformation, pSysCycleTime, ulSize, NULL); if (!NT_SUCCESS(status)) { break; } for (ULONG i = 0; i < ulNumberOfProcessors; i++) { ullTotal += pSysCycleTime[i].QuadPart; } PhUpdateDelta(&g_phCpuCycleDelta, ullTotal); ullRet = g_phCpuCycleDelta.Delta; } while (0); if (NULL != pSysCycleTime) { free(pSysCycleTime); } return ullRet; } //獲取程序累計時間 inline ULONGLONG GetProcessCycleTime(HANDLE hProcessHandle) { NTSTATUS status; ULONGLONG ullRet = 0; PPROCESS_CYCLE_TIME_INFORMATION pProcessCycleTime = NULL; do { if (NULL == NtQueryInformationProcess) { NtQueryInformationProcess = (NTQUERYINFORMATIONPROCESS)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess"); if (NULL == NtQueryInformationProcess) { break; } } ULONG ulNeedSize = 0; status = NtQueryInformationProcess(hProcessHandle, ProcessCycleTime, NULL, 0, &ulNeedSize); if (!NT_SUCCESS(status)) { break; } pProcessCycleTime = (PPROCESS_CYCLE_TIME_INFORMATION)malloc(ulNeedSize); if (NULL == pProcessCycleTime) { break; } status = NtQueryInformationProcess(hProcessHandle, ProcessCycleTime, pProcessCycleTime, ulNeedSize, NULL); if (!NT_SUCCESS(status)) { break; } ullRet = pProcessCycleTime->AccumulatedCycles; } while (0); if (NULL != pProcessCycleTime) { free(pProcessCycleTime); } return ullRet; } //獲取已退出程序在該時間段內使用的週期時間 inline ULONGLONG GetDeadProcessCycleTime(const std::vector<PROCESS_ITEM_INFORMATION> &oldVecProInfos, const std::vector<PROCESS_ITEM_INFORMATION> &newVecProInfos) { ULONGLONG ullRet = 0; for (std::vector<PROCESS_ITEM_INFORMATION>::const_iterator iter = oldVecProInfos.begin(); iter != oldVecProInfos.end(); ++ iter) { PROCESS_ITEM_INFORMATION oldProcess = *iter; PROCESS_ITEM_INFORMATION newProcess = {0}; if (GetProcessInfo(newVecProInfos, oldProcess.ProcessId, oldProcess.CreateTime, newProcess)) { HANDLE hProcess = GetProcessHandle((HANDLE)oldProcess.ProcessId); ULONGLONG ullCycleTime = GetProcessCycleTime(hProcess); ullRet += ullCycleTime; } } return ullRet; } //計算程序的CPU佔用率 inline void CalProcessCPUUsage(std::map<ULONG, double> &mapPros) { PVOID pProcInfo = NULL; PSYSTEM_PROCESS_INFORMATION pProcess =NULL; ULONG ulNeedSize = 0; ULONG64 sysTotalCycleTime = 0; std::map<ULONG, ULONG64> mapCycleTime; std::vector<PROCESS_ITEM_INFORMATION> vecSysProInfos; if (NULL == NtQuerySystemInformation) { NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation"); if (NULL == NtQuerySystemInformation) { return; } } NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &ulNeedSize); pProcInfo = (PVOID)malloc(ulNeedSize); NtQuerySystemInformation(SystemProcessInformation, pProcInfo, ulNeedSize, NULL); pProcess = FIND_FIRST_PROCESS(pProcInfo); while(pProcess) { //Idle程序 if (pProcess->UniqueProcessId == SYSTEM_IDLE_PROCESS_ID) { pProcess->CycleTime = GetCpuIdleCycleTime(); } //所以當前程序 PROCESS_ITEM_INFORMATION ProcessItem = {0}; if (GetProcessInfo(g_vecSysProInfos, (ULONG64)pProcess->UniqueProcessId, pProcess->CreateTime.QuadPart, ProcessItem)) { sysTotalCycleTime += pProcess->CycleTime - ProcessItem.CycleTime; for (std::map<ULONG, double>::iterator iter = mapPros.begin(); iter != mapPros.end(); ++ iter) { ULONG ulPid = iter->first; if ((ULONGLONG)pProcess->UniqueProcessId == ulPid) { ULONG64 cycleTime = pProcess->CycleTime - ProcessItem.CycleTime; mapCycleTime[ulPid] = cycleTime; } } } else { sysTotalCycleTime += pProcess->CycleTime; for (std::map<ULONG, double>::iterator iter = mapPros.begin(); iter != mapPros.end(); ++ iter) { ULONG ulPid = iter->first; if ((ULONGLONG)pProcess->UniqueProcessId == ulPid) { ULONG64 cycleTime = pProcess->CycleTime; mapCycleTime[ulPid] = cycleTime; } } } //HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pProcess->UniqueProcessId); //g_mapProInfos[pProcess->UniqueProcessId] = hProcess; PROCESS_ITEM_INFORMATION process = {0}; process.CreateTime = pProcess->CreateTime.QuadPart; process.CycleTime = pProcess->CycleTime; process.ProcessId = (ULONG64)pProcess->UniqueProcessId; vecSysProInfos.push_back(process); pProcess = FIND_NEXT_PROCESS(pProcess); } //核心時間(DPC以及中斷服務時間) ULONGLONG ullSysCycleTime = GetCpuSystemCycleTime(); ////已退出程序使用時間 //ULONGLONG ullDeadProCycleTime = GetDeadProcessCycleTime(g_vecSysProInfos, vecSysProInfos); sysTotalCycleTime += ullSysCycleTime; //sysTotalCycleTime += ullDeadProCycleTime; g_vecSysProInfos.clear(); for (std::vector<PROCESS_ITEM_INFORMATION>::iterator iter = vecSysProInfos.begin(); iter != vecSysProInfos.end(); ++ iter) { g_vecSysProInfos.push_back(*iter); } //先清空下 mapPros.clear(); for (std::map<ULONG, ULONG64>::iterator iter = mapCycleTime.begin(); iter != mapCycleTime.end(); ++ iter) { ULONG ulPid = iter->first; ULONG64 ullCycleTime = iter->second; if (ullCycleTime != 0 && sysTotalCycleTime != 0) { double dUsage = 100 * (double)ullCycleTime / (double)sysTotalCycleTime; mapPros[ulPid] = dUsage; } } if (NULL != pProcInfo) { free(pProcInfo); } }
程序CPU佔用率= 程序消耗CPU時間/ 所有程序消耗CPU總時間*100%
CycleTime:週期時間(即從程序啟動開始到當前所消耗的CPU時間總和)
所有程序消耗CPU總時間= 所有程序的週期時間+核心時間(DPC以及中斷服務)
Idle程序消耗的CPU時間= 每個核消耗的CPU時間相加