c++實現Windows內存監視

問題描述

設計一個內存監視器,能實時地顯示當前系統中內存的使用狀況,包括系統地址空間的佈局,物理內存的使用狀況;能實時顯示某個進程的虛擬地址空間佈局和工做集信息等。ios

思路

獲取系統信息

  1. SYSTEM_INFOc++

    typedef struct _SYSTEM_INFO {
      union {
        DWORD dwOemId;
        struct {
          WORD wProcessorArchitecture;
          WORD wReserved;
        } DUMMYSTRUCTNAME;
      } DUMMYUNIONNAME;
      DWORD     dwPageSize;
      LPVOID    lpMinimumApplicationAddress;
      LPVOID    lpMaximumApplicationAddress;
      DWORD_PTR dwActiveProcessorMask;
      DWORD     dwNumberOfProcessors;
      DWORD     dwProcessorType;
      DWORD     dwAllocationGranularity;
      WORD      wProcessorLevel;
      WORD      wProcessorRevision;
    } SYSTEM_INFO, *LPSYSTEM_INFO;
  2. GetNativeSystemInfowindows

    注意INTELx86_64體系最好用這個函數。其餘的等價於GetSystemInfoapi

    void GetNativeSystemInfo(
      LPSYSTEM_INFO lpSystemInfo
    );
    • LPSYSTEM_INFO 指向SYSTEM_INFO的指針
  3. 信息輸出數據結構

    DWORD mem_size = (DWORD*)si.lpMaximumApplicationAddress - (DWORD*)si.lpMinimumApplicationAddress;
     printDword(L"處理器個數                ", si.dwNumberOfProcessors);
     printStrFormatByte(L"物理頁大小                ", si.dwPageSize);
     printAddress(L"進程最小尋址空間:        0x", si.lpMinimumApplicationAddress);
     printAddress(L"進程最大尋址地址:         0x", si.lpMaximumApplicationAddress);
     printStrFormatByte(L"進程可用空間大小:        ", mem_size);

    注意這裏的print***是自定義函數。app

  4. 展現函數

    image_32

獲取物理內存信息

主要使用到的數據結構和函數有MEMORYSTATUSEXGlobalMemoryStatusEx, PERFORMANCE_INFORMATIONGetPerformanceInfo佈局

  1. MEMORYSTATUSEXspa

    typedef struct _MEMORYSTATUSEX {
      DWORD     dwLength;
      DWORD     dwMemoryLoad;
      DWORDLONG ullTotalPhys;
      DWORDLONG ullAvailPhys;
      DWORDLONG ullTotalPageFile;
      DWORDLONG ullAvailPageFile;
      DWORDLONG ullTotalVirtual;
      DWORDLONG ullAvailVirtual;
      DWORDLONG ullAvailExtendedVirtual;
    } MEMORYSTATUSEX, *LPMEMORYSTATUSEX;

    注意在使用該數據結構以前,dwLength必須進行指定,dwLength = sizeof(MEMORYSTATUSEX)線程

  2. GlobalMemoryStatusEx

    BOOL GlobalMemoryStatusEx(
      LPMEMORYSTATUSEX lpBuffer
    );
    • lpBuffer 是指向MEMORYSTAUSEX的指針,用於保存信息。
  3. PERFORMANCE_INFORMATION

    typedef struct _PERFORMANCE_INFORMATION {
      DWORD  cb;
      SIZE_T CommitTotal;
      SIZE_T CommitLimit;
      SIZE_T CommitPeak;
      SIZE_T PhysicalTotal;
      SIZE_T PhysicalAvailable;
      SIZE_T SystemCache;
      SIZE_T KernelTotal;
      SIZE_T KernelPaged;
      SIZE_T KernelNonpaged;
      SIZE_T PageSize;
      DWORD  HandleCount;
      DWORD  ProcessCount;
      DWORD  ThreadCount;
    } PERFORMANCE_INFORMATION, *PPERFORMANCE_INFORMATION, PERFORMACE_INFORMATION, *PPERFORMACE_INFORMATION;
  4. GetPerformanceInfo

    BOOL GetPerformanceInfo(
      PPERFORMANCE_INFORMATION pPerformanceInformation,
      DWORD                    cb
    );
    • pPerformanceInformation 指向用於保存返回信息的指針
    • cb 須要指明PERFORMANCE_INFORMATION結構體的大小
  5. 效果展現

    1577537259368

獲取全部進程的基本信息

主要過程是先獲取全部進程的一個snapshot,因爲進程信息和數量是動態變化的,因此須要先獲取一個靜態的信息集;其次,相似於目錄檢索對snapshot進行順序檢索,獲取進程信息。

建立進程snapshot

  1. CreateToolhelp32Snapshot

    HANDLE CreateToolhelp32Snapshot(
      DWORD dwFlags,
      DWORD th32ProcessID
    );
    • DWORD dwFlags 用於代表該函數獲取多少有關屬性到snapshot中。具體說明可參考MSDN, 這裏選用的參數是TH32CS_SNAPALL
    • DWORD th32ProcessID 須要獲取的進程的pid。0表示是最近的進程。

遍歷進程

數據結構:PROCESSENTRY32

API: Process32First Process32Next

  1. PROCESSENTRY32

    typedef struct tagPROCESSENTRY32 {
      DWORD     dwSize;
      DWORD     cntUsage;
      DWORD     th32ProcessID;
      ULONG_PTR th32DefaultHeapID;
      DWORD     th32ModuleID;
      DWORD     cntThreads;
      DWORD     th32ParentProcessID;
      LONG      pcPriClassBase;
      DWORD     dwFlags;
      TCHAR     szExeFile[MAX_PATH];
    } PROCESSENTRY32, *PPROCESSENTRY32;

    注意在使用前必須指定dwSize = sizeof(PROCESSENTRY32)

  2. Process32First

    BOOL Process32First(
      HANDLE           hSnapshot,
      LPPROCESSENTRY32 lppe
    );
    • HANDEL hSnapshot 就是從上述CreateToolhelp32Snapshot 得到的。
    • LPPROCESSENTRY32 lppe 就是PROCESSENTRY32 的指針
  3. Process32Next

    BOOL Process32Next(
      HANDLE           hSnapshot,
      LPPROCESSENTRY32 lppe
    );

    含義同上。其中hSnapshot 是同一個,不一樣的是lppe 此時有了值,用於保存當前項的下一項的進程的狀態信息。

效果展現

1577539899792

獲取單個進程的詳細信息

使用到的主要數據結構有:SYSTEM_INFO (已介紹),MEMORY_BASIC_INFORMATION,

使用到的主要API有:GetNativeSystemInfo(已介紹),VirtualQueryEx, OpenProcess

  1. MEMORY_BASIC_INFORMATION

    typedef struct _MEMORY_BASIC_INFORMATION {
      PVOID  BaseAddress;
      PVOID  AllocationBase;
      DWORD  AllocationProtect;
      SIZE_T RegionSize;
      DWORD  State;
      DWORD  Protect;
      DWORD  Type;
    } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
    • PVOID BaseAddress 頁面區域的基地址
    • DWORD AllocationProtect; 若是這個頁面區域是初始獲取的,這裏就是其頁面區域的保護方式
    • SIZE_T RegionSize 當前塊的大小
    • DWORD State 當前頁面塊的狀態,MEM_COMMIT MEME_FREE MEM_RESERVE 三種狀態
    • DWORD Protect 當前塊中的頁面訪問方式
    • DWORD Type 塊類型,三種MEM_IMAGE MEM_MAPPED MEM_PRIVATE
  2. OpenProcess

    HANDLE OpenProcess(
      DWORD dwDesiredAccess,
      BOOL  bInheritHandle,
      DWORD dwProcessId
    );
    • DWORD dwDesiredAccess 訪問該進程的方式,我這裏選用的是PROCESS_ALL_ACCESS
    • BOOL bInheritHandle 若是爲真,該進程的子進程也將繼承該函數的返回句柄
    • DWORD dwProcessId 要打開的進程的PID
  3. VirtualQueryEx

    SIZE_T VirtualQueryEx(
      HANDLE                    hProcess,
      LPCVOID                   lpAddress,
      PMEMORY_BASIC_INFORMATION lpBuffer,
      SIZE_T                    dwLength
    );
    • hProcess 要查詢的進程的句柄
    • lpAddress 要查詢的進程的虛存的塊的基地址
    • lpBuffer 指向要保存相關信息的數據的指針就是上文提到的MEMORY_BASIC_INFORMATION
    • dwLength = sizeof(MEMORY_BASIC_INFORMATION)

1577541562291

效果展現

實現的功能以下,具體的展現已在上文說明。

1577544315864

源代碼

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <tchar.h>
#include <iomanip>   
#include <Windows.h> 
#include <Tlhelp32.h>  
#include <shlwapi.h>
#include <Psapi.h>
#pragma comment(lib,"Shlwapi.lib") 
#pragma comment(lib, "Psapi.Lib")
#pragma comment(lib,"Kernel32.lib")

using namespace std;

enum ProgramState { QUERY_SYS_INFO, QUERY_MEM_INFO, QUERY_PRE_INFO, EXIT };

//查詢系統配置信息
void getSystemInfo();

//物理內存使用狀況
void getMemoryInfo();

//打印全部進程的基本信息
void getProcessInfo();

//得到單個內存的使用狀況
void showSingleProcessMemDetail(int PID);

void help();

int main()
{
    setlocale(LC_ALL, "");
    int state = ProgramState::EXIT;//默認爲退出狀態
    while (1)
    {
        help();
        std::cout.fill(' ');
        std::cout.setf(ios::dec);//確保cout輸出爲十進制
        cin >> state;
        std::cout << "\n";
        if (state == ProgramState::QUERY_SYS_INFO)
        {
            getSystemInfo();
        }
        else if (state == ProgramState::QUERY_MEM_INFO)
        {
            getMemoryInfo();
        }
        else if (state == ProgramState::QUERY_PRE_INFO)
        {
            getProcessInfo();  //當前全部運行進程基本信息

            std::cout << "輸入進程PID以查看其虛擬內存信息" << endl;
            int PID;
            cin >> PID;
            
            showSingleProcessMemDetail(PID);
        }
        else if (state == ProgramState::EXIT)
        {
            return 0;    //結束程序的運行
        }
    }
    return 0;
}


//將字節數轉爲字符串打印輸出
inline void printStrFormatByte(const WCHAR* info, DWORDLONG bytes)
{
    TCHAR tmp[MAX_PATH];
    ZeroMemory(tmp, sizeof(tmp));
    StrFormatByteSize(bytes, tmp, MAX_PATH);
    wcout << info << tmp << endl;
    return;
}

//打印地址
inline void printAddress(const WCHAR* info, LPVOID addr)
{
    wcout << info << hex << setw(8) << addr << endl;
}

inline void printDword(const WCHAR* info, DWORDLONG dw)//將DWORD轉爲DWORDLONG
{
    wcout << info;
    std::cout << dw << endl;
}

//查詢系統配置信息
void getSystemInfo()
{
    SYSTEM_INFO si;
    ZeroMemory(&si, sizeof(si));
    GetNativeSystemInfo(&si);
    DWORD mem_size = (DWORD*)si.lpMaximumApplicationAddress - (DWORD*)si.lpMinimumApplicationAddress;
    printDword(L"處理器個數                ", si.dwNumberOfProcessors);
    printStrFormatByte(L"物理頁大小                ", si.dwPageSize);
    printAddress(L"進程最小尋址空間:        0x", si.lpMinimumApplicationAddress);
    printAddress(L"進程最大尋址地址:         0x", si.lpMaximumApplicationAddress);
    printStrFormatByte(L"進程可用空間大小:        ", mem_size);
    return;
}

//物理內存使用狀況
void getMemoryInfo()
{
    MEMORYSTATUSEX mem_stat;
    ZeroMemory(&mem_stat, sizeof(mem_stat));
    mem_stat.dwLength = sizeof(mem_stat);//必須執行這一步
    GlobalMemoryStatusEx(&mem_stat); //取得內存狀態
    std::cout << "內存利用率        \t" << mem_stat.dwMemoryLoad << endl;
    printStrFormatByte(L"物理內存:        \t", mem_stat.ullTotalPhys);
    printStrFormatByte(L"可用物理內存:      \t", mem_stat.ullAvailPhys);
    printStrFormatByte(L"總共頁文件大小:    \t", mem_stat.ullTotalPageFile);
    printStrFormatByte(L"空閒頁文件大小:    \t", mem_stat.ullAvailPageFile);
    printStrFormatByte(L"虛擬內存大小:    \t", mem_stat.ullTotalVirtual);
    printStrFormatByte(L"空閒虛擬內存大小:\t", mem_stat.ullAvailVirtual);
    printStrFormatByte(L"空閒拓展內存大小:\t", mem_stat.ullAvailExtendedVirtual);

    cout << endl << endl;

    PERFORMANCE_INFORMATION pi;
    GetPerformanceInfo(&pi, sizeof(pi));
    DWORDLONG page_size = pi.PageSize;
    printStrFormatByte(L"Commit Total           \t", pi.CommitTotal * page_size);
    printStrFormatByte(L"Commit Limit           \t", pi.CommitLimit * page_size);
    printStrFormatByte(L"Commit Peak            \t", pi.CommitPeak * page_size);
    printStrFormatByte(L"Physical Memory        \t", pi.PhysicalTotal * page_size);
    printStrFormatByte(L"Physical Memory Avaliable   ", pi.PhysicalAvailable * page_size);
    printStrFormatByte(L"System Cache           \t", page_size*pi.SystemCache);
    printStrFormatByte(L"Kerbel Total           \t", page_size * pi.KernelTotal);
    printStrFormatByte(L"Kernel Paged           \t", page_size * pi.KernelPaged);
    printStrFormatByte(L"Kernel Nonpaged        \t", page_size * pi.KernelNonpaged);
    printStrFormatByte(L"Page Size              \t", page_size * pi.PageSize);
    printDword(L"Handle Count           \t", page_size * pi.HandleCount);
    printDword(L"Process Count          \t", page_size * pi.ProcessCount);
    printDword(L"Thread Count           \t", page_size * pi.ThreadCount);
    


}

//打印全部進程的基本信息
void getProcessInfo()
{
    //建立進程snapshot
    HANDLE h_process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
    if (h_process_snapshot == INVALID_HANDLE_VALUE)
    {
        cout << "CreateToolhelp32Snapshot調用失敗!\n";
        exit(-1);
    }


    PROCESSENTRY32 process_entry;
    process_entry.dwSize = sizeof(process_entry);//必需要指定大小

    //循環遍歷輸出全部進程的信息
    if (Process32First(h_process_snapshot, &process_entry))
    {
        wcout << setiosflags(ios::left) << setw(40) << L"Process Name";
        cout << setiosflags(ios::right) << setw(15) << "PID";
        wcout << L"\t\t線程數量" << endl << endl;
        do
        {
            wcout << setiosflags(ios::left) << setw(40) << process_entry.szExeFile;//進程名
            std::cout << "\t" << setw(7) << process_entry.th32ProcessID;//pid
            std::cout << "  \t" << setw(3) << process_entry.cntThreads << endl;//線程數目
        } while (Process32Next(h_process_snapshot, &process_entry));
    }
    CloseHandle(h_process_snapshot);
}

//顯示當前塊頁面訪問方式
void printPageProtection(DWORD dwTarget)
{
    const int width = 20;
    switch (dwTarget)
    {
    case(PAGE_READONLY):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "READONLY";
        break;

    }
    case(PAGE_GUARD):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "GUARD";
        break;

    }
    case(PAGE_NOCACHE):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "NOCACHE";
        break;

    }
    case(PAGE_NOACCESS):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "NOACCESS";
        break;

    }
    case(PAGE_READWRITE):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "READWRITE";
        break;

    }
    case(PAGE_WRITECOPY):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "WRITECOPY";
        break;

    }
    case(PAGE_EXECUTE):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "EXECUTE";
        break;

    }
    case(PAGE_EXECUTE_READ):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "EXECUTE_READ";
        break;

    }
    case(PAGE_EXECUTE_READWRITE):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "EXECUTE_READWRITE";
        break;

    }
    case(PAGE_EXECUTE_WRITECOPY):
    {
        std::cout << setiosflags(ios::left) << setw(width) << "EXECUTE_WRITECOPY";
        break;
    }
    default:
        break;
    }

}

//輸出單個進程的詳細信息
void showSingleProcessMemDetail(int PID)
{
    SYSTEM_INFO si;
    ZeroMemory(&si, sizeof(si));
    GetNativeSystemInfo(&si);           //得到系統信息 

    //循環訪問整個進程地址空間 
    LPCVOID p_begin = (LPVOID)si.lpMinimumApplicationAddress; //p_begin指向開始的地址
    std::cout.setf(ios::left);
    //輸出表頭
    wcout << setiosflags(ios::left) << setw(21) << L"塊地址"
        << setw(10) << L"塊大小"
        << setw(10) << L"塊內頁狀態"
        << setw(12) << L"塊內頁保護方式"
        << setw(10) << L"塊類型" << endl;

    HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, 0, PID); //獲得PID的值
    if (h_process == INVALID_HANDLE_VALUE)
    {
        std::cout << "Failed to OpenProcess" << endl;
        exit(-1);
    }

    MEMORY_BASIC_INFORMATION mem;    //虛擬內存空間的基本信息結構 
    ZeroMemory(&mem, sizeof(mem));
    while (p_begin < (LPVOID)si.lpMaximumApplicationAddress)
    {
        //查詢進程在p_begin開始的塊信息
        VirtualQueryEx(
            h_process,                       //進程句柄
            p_begin,                         //開始位置的地址
            &mem,                           //緩衝區
            sizeof(mem));
        //塊結束地址 
        LPCVOID p_end = (PBYTE)p_begin + mem.RegionSize;

        //輸出塊起始、結束地址
        std::cout << hex << setw(8) << setfill('0') << (DWORD*)p_begin
            << "-"
            << hex << setw(8) << setfill('0') << (DWORD*)p_end;

        //輸出塊大小
        TCHAR tmp[MAX_PATH];
        ZeroMemory(tmp, sizeof(tmp));
        StrFormatByteSize(mem.RegionSize, tmp, MAX_PATH);
        std::wcout << "\t" << setw(8) << tmp;

        //輸出塊的狀態 
        std::cout.fill(' ');
        if (mem.State == MEM_COMMIT)
        {
            std::cout << setw(10) << "已提交";
        }
        else if (mem.State == MEM_FREE)
        {
            std::cout << setw(10) << "空閒";

        }
        else if (mem.State == MEM_RESERVE)
        {
            std::cout << setw(10) << "保留";
        }

        //顯示塊內頁的保護方式
        if (mem.Protect == 0 && mem.State != MEM_FREE)
        {
            mem.Protect = PAGE_READONLY;
        }
        printPageProtection(mem.Protect);

        //顯示塊的類型 鄰近頁面物理存儲器類型指的是與給定地址所在頁面相同的存儲器類型
        std::cout.fill(' ');
        if (mem.Type == MEM_IMAGE)
        {
            std::cout << "\t\tImage";
        }
        else if (mem.Type == MEM_PRIVATE)
        {
            std::cout << "\t\tPrivate";
        }
        else if (mem.Type == MEM_MAPPED)
        {
            std::cout << "\t\tMapped";
        }
        cout << endl;

        //移動塊指針得到下一個塊 
        if (p_begin == p_end)//部分進程如0號進程沒法進行空間遍歷
            break;
        p_begin = p_end;
    }
}

void help()
{
    std::cout << "\n\nMenu:" << endl
        << ProgramState::QUERY_SYS_INFO << " - 查看系統信息" << endl
        << ProgramState::QUERY_MEM_INFO << " - 查看內存狀況" << endl
        << ProgramState::QUERY_PRE_INFO << " - 查看當前運行進程信息及其虛擬地址空間佈局和工做集信息" << endl
        << ProgramState::EXIT << " - 退出\n\n";
}
相關文章
相關標籤/搜索