windows平臺進程CPU佔用率的計算

在進程的性能數據採集過程中,經常用到的一個性能指標就是進程的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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章