設計一個內存監視器,能實時地顯示當前系統中內存的使用狀況,包括系統地址空間的佈局,物理內存的使用狀況;能實時顯示某個進程的虛擬地址空間佈局和工做集信息等。ios
SYSTEM_INFO
c++
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;
GetNativeSystemInfo
windows
注意INTELx86_64體系最好用這個函數。其餘的等價於GetSystemInfo
api
void GetNativeSystemInfo( LPSYSTEM_INFO lpSystemInfo );
LPSYSTEM_INFO
指向SYSTEM_INFO
的指針信息輸出數據結構
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
展現函數
主要使用到的數據結構和函數有MEMORYSTATUSEX
與 GlobalMemoryStatusEx
, PERFORMANCE_INFORMATION
與 GetPerformanceInfo
佈局
MEMORYSTATUSEX
spa
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)
線程
GlobalMemoryStatusEx
BOOL GlobalMemoryStatusEx( LPMEMORYSTATUSEX lpBuffer );
lpBuffer
是指向MEMORYSTAUSEX
的指針,用於保存信息。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;
GetPerformanceInfo
BOOL GetPerformanceInfo( PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb );
pPerformanceInformation
指向用於保存返回信息的指針cb
須要指明PERFORMANCE_INFORMATION
結構體的大小效果展現
主要過程是先獲取全部進程的一個snapshot,因爲進程信息和數量是動態變化的,因此須要先獲取一個靜態的信息集;其次,相似於目錄檢索對snapshot進行順序檢索,獲取進程信息。
CreateToolhelp32Snapshot
HANDLE CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );
DWORD dwFlags
用於代表該函數獲取多少有關屬性到snapshot中。具體說明可參考MSDN, 這裏選用的參數是TH32CS_SNAPALL
DWORD th32ProcessID
須要獲取的進程的pid。0表示是最近的進程。數據結構:PROCESSENTRY32
API: Process32First
Process32Next
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)
Process32First
BOOL Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
HANDEL hSnapshot
就是從上述CreateToolhelp32Snapshot
得到的。LPPROCESSENTRY32 lppe
就是PROCESSENTRY32
的指針Process32Next
BOOL Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
含義同上。其中hSnapshot
是同一個,不一樣的是lppe
此時有了值,用於保存當前項的下一項的進程的狀態信息。
使用到的主要數據結構有:SYSTEM_INFO
(已介紹),MEMORY_BASIC_INFORMATION
,
使用到的主要API有:GetNativeSystemInfo
(已介紹),VirtualQueryEx
, OpenProcess
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
OpenProcess
HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );
DWORD dwDesiredAccess
訪問該進程的方式,我這裏選用的是PROCESS_ALL_ACCESS
BOOL bInheritHandle
若是爲真,該進程的子進程也將繼承該函數的返回句柄DWORD dwProcessId
要打開的進程的PIDVirtualQueryEx
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)
實現的功能以下,具體的展現已在上文說明。
#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"; }