问题描述
设计一个内存监视器,能实时地显示当前系统中内存的使用情况,包括系统地址空间的布局,物理内存的使用情况;能实时显示某个进程的虚拟地址空间布局和工作集信息等。
思路
获取系统信息
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;
GetNativeSystemInfo
注意INTELx86_64体系最好用这个函数。其他的等价于
GetSystemInfo
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***
是自定义函数。展示
获取物理内存信息
主要使用到的数据结构和函数有MEMORYSTATUSEX
与 GlobalMemoryStatusEx
, PERFORMANCE_INFORMATION
与 GetPerformanceInfo
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)
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进行顺序检索,获取进程信息。
创建进程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
要打开的进程的PID
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)
效果展示
实现的功能如下,具体的展示已在上文说明。
源代码
#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"; }
来源:https://www.cnblogs.com/harrypotterjackson/p/12113387.html