c++實現Windows內存監視


問題描述

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

思路

獲取系統信息

  1. SYSTEM_INFO

    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. GetNativeSystemInfo

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

    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***是自定義函數。

  4. 展示

    image_32

獲取物理內存信息

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

  1. MEMORYSTATUSEX

    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";
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM