在進程的性能數據採集過程中,經常用到的一個性能指標就是進程的cpu佔用率,下面給出它的計算方法及示例代碼。
1、進程CPU佔用率的定義
進程CPU佔用率:指進程在一個時間段內消耗的CPU時間與該時間段長度的比值。
2、進程CPU佔用率計算方法
根據上述定義,可以得到進程CPU佔用率計算公式如下:
進程消耗的CPU時間 = 進程消耗的內核態時間 + 進程消耗的用戶態時間,即 costTime = kernelTime + UserTime
進程的CPU佔用率 = 進程消耗的CPU時間 / 刷新週期 / CPU核數
計算線程的CPU佔用率,要不要考慮CPU核數呢?在Process Explorer中的目標進程的屬性頁面,在Threads標籤頁下能看到目標進程中所有線程的CPU佔用情況,如下圖所示:
3、CPU佔用率計算涉及到的API
示例程序用到的主要API
GetSystemInfo 我們主要用它來獲取系統中CPU核心個數
OpenProcess 用來打開指定進程的句柄
GetProcessTimes 根據OpenProcess返回的句柄,獲取進程的KernelTime和UserTime(可用於進程的CPU佔用率計算)
其它API(用於線程等其它情況下的計算)
OpenThread 獲取指定線程的句柄
GetThreadTimes 根據OpenThread返回的句柄,獲取線程的KernelTime和UserTime (可用於線程的CPU佔用率計算)
GetSystemTimes 獲取總的CPU時間IdleTime、KernelTime和UserTime,可用於系統總的CPU佔用率計算(注:多核CPU中返回的是所有CPU核時間的總和)
NtQuerySystemInformation 這是個native api,可以獲取到許多信息
4、示例代碼
#include "stdafx.h"
#include <conio.h>
#include <windows.h>
#include <TlHelp32.h>
#include <process.h>
#define MY_PROCESS_ERROR(Condition) do{ if (!(Condition)) goto Exit0; } while (false)
static DWORD g_sdwTickCountOld = 0; // 上一次的tick計數
static LARGE_INTEGER g_slgProcessTimeOld; // 保存進程上一次的時間佔用
static DWORD g_sdwProcessorCoreNum = 0; // 處理器核心數
static HANDLE g_shExitEvent = NULL; // 線程退出控制
typedef struct _TARGET_PROCESS
{
DWORD dwProcessId; // 進程ID
}TARGET_PROCESS, *PTARGET_PROCESS;
/*@brief 獲取找到的與指定進程名相符的第一個進程ID
* @param [in] cpszExeFileName 進程可執行文件名(不帶路徑)
* @param [in/out] dwPID 返回找到的名字符合的第一個進程ID
* @return 成功 : S_OK 失敗 : 錯誤碼
*/
HRESULT FindFirstProcessIdByName(const TCHAR* cpszExeFileName, DWORD &dwPID)
{
HRESULT hr = E_FAIL;
PROCESSENTRY32 pe = { 0 };
HANDLE hSnapshot = NULL;
if (NULL == cpszExeFileName)
{
hr = HRESULT_FROM_WIN32(ERROR_BAD_ARGUMENTS);
goto Exit0;
}
pe.dwSize = sizeof(PROCESSENTRY32);
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit0;
}
if (FALSE == Process32First(hSnapshot, &pe))
{
hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES);
goto Exit0;
}
hr = S_FALSE;
do
{
if (0 == _tcsicmp(cpszExeFileName, pe.szExeFile))
{
dwPID = pe.th32ProcessID;
hr = S_OK;
break;
}
}while(Process32Next(hSnapshot, &pe));
Exit0:
if(hSnapshot)
{
CloseHandle(hSnapshot);
hSnapshot = NULL;
}
return hr;
}
/*@brief 獲取進程的Cpu佔用率
* @param [in] hProcess 進程句柄
* @param [in] dwElepsedTime 取樣間隔時間(毫秒)
* @return 成功 : cpu佔用率 失敗 : -1
*/
int GetProcessCpuPercent(const HANDLE hProcess, const DWORD dwElepsedTime)
{
int nProcCpuPercent = 0;
BOOL bRetCode = FALSE;
FILETIME CreateTime, ExitTime, KernelTime,UserTime;
LARGE_INTEGER lgKernelTime;
LARGE_INTEGER lgUserTime;
LARGE_INTEGER lgCurTime;
bRetCode = GetProcessTimes(hProcess, &CreateTime, &ExitTime, &KernelTime, &UserTime);
if (bRetCode)
{
lgKernelTime.HighPart = KernelTime.dwHighDateTime;
lgKernelTime.LowPart = KernelTime.dwLowDateTime;
lgUserTime.HighPart = UserTime.dwHighDateTime;
lgUserTime.LowPart = UserTime.dwLowDateTime;
lgCurTime.QuadPart = (lgKernelTime.QuadPart + lgUserTime.QuadPart) / 10000;
nProcCpuPercent = (int)((lgCurTime.QuadPart - g_slgProcessTimeOld.QuadPart) * 100 / dwElepsedTime);
g_slgProcessTimeOld = lgCurTime;
nProcCpuPercent = nProcCpuPercent / g_sdwProcessorCoreNum;
}
else
{
nProcCpuPercent = -1;
}
return nProcCpuPercent;
}
unsigned __stdcall WorkerThread(void *pArg)
{
HRESULT hr = E_FAIL;
HANDLE hProcess = NULL;
DWORD dwProcessId = 0;
DWORD dwRetVal = 0;
DWORD dwCurrentTickCount = 0;
DWORD dwElapsedTime = 0;
int nProcessCpuPercent = 0;
TARGET_PROCESS *pTargetProcess = (TARGET_PROCESS *)pArg;
dwProcessId = pTargetProcess->dwProcessId;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION , FALSE, dwProcessId);
MY_PROCESS_ERROR(hProcess);
do
{
dwRetVal = WaitForSingleObject(g_shExitEvent, 1000);
if (WAIT_OBJECT_0 == dwRetVal ||
WAIT_FAILED == dwRetVal
)
{
break;
}
dwCurrentTickCount = GetTickCount();
dwElapsedTime = dwCurrentTickCount - g_sdwTickCountOld;
g_sdwTickCountOld = dwCurrentTickCount;
nProcessCpuPercent = GetProcessCpuPercent(hProcess, dwElapsedTime);
wprintf(L"cpu = %d\n", nProcessCpuPercent);
} while (1);
Exit0:
if (hProcess)
{
CloseHandle(hProcess);
hProcess = NULL;
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = E_FAIL;
HANDLE hThread = NULL;
unsigned int uiTid = 0;
SYSTEM_INFO sysInfo = { 0 };
TARGET_PROCESS struTargetProcess;
if (argc > 1)
{
hr = FindFirstProcessIdByName(argv[1], struTargetProcess.dwProcessId);
if (S_OK != hr)
{
_tprintf(_T("Can't find process \'%s\' ret=0x%X\n"), argv[1], hr);
goto Exit0;
}
GetSystemInfo(&sysInfo);
g_sdwProcessorCoreNum = sysInfo.dwNumberOfProcessors;
g_shExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
MY_PROCESS_ERROR(g_shExitEvent);
hThread = (HANDLE)_beginthreadex(NULL, 0, WorkerThread, &struTargetProcess, 0, &uiTid);
MY_PROCESS_ERROR(hThread);
_getch();
SetEvent(g_shExitEvent);
WaitForSingleObject(hThread, INFINITE);
}
else
{
_tprintf(_T("Please input a process name.\n"));
}
Exit0:
if (hThread)
{
CloseHandle(hThread);
hThread = NULL;
}
if (g_shExitEvent)
{
CloseHandle(g_shExitEvent);
g_shExitEvent = NULL;
}
return 0;
}