内存监视

来源:互联网 发布:php生成8位唯一邀请码 编辑:程序博客网 时间:2024/05/19 20:23

一、实验目的

熟悉Windows存储器管理中提供的各种机制和实现的请求调页和群集技术。

Windows提供给应用程序的内存方式具有统一的简明和保护性的特点。另外,用户不需要知道操作系统如何分配内存,只需要知道应用程序如何分配内存即可。

通过实验,了解Windows内存结构和虚拟内存的管理,学习如何在应用程序中管理内存,体会Windows应用程序使用内存的简单性。了解当前系统中内存的使用情况,包括系统地址空间的布局和物理内存的使用情况;可以显示某个进程的虚拟地址空间和工作集信息等。

二、实验内容

在Windows平台上设计一个内存监视器,能实时地显示当前系统中内存的使用情况,包括系统地址空间的布局,物理内存的使用情况;能实时显示某个进程的虚拟地址空间布局和工作集信息等。

相关的系统调用:

GetSystemInfo,VirtualQueryEx, VirtualAlloc, GetPerformanceInfo,

GlobalMemoryStatusEx…

三、实验环境

硬件环境(如图1所示):

图1

软件环境(如图2所示):


图2

四、实验原理

Windows2000/XP是32位的操作系统,在Windows下运行的每个应用程序都认为能独占4GB的虚拟地址空间。其中,低2GB为进程私有的地址空间,用来存放用于程序和DLL,高2GB为所有进程共享区,也就是操作系统占用区。事实上,很少有进程能够占用2GB的存储空间。Windows把每个进程的虚拟内存地址映射为物理内存地址。

物理内存指的是计算机配置的RAM,系统可以管理所有的物理内存。Windows通过分配RAM、页面文件或者两者中的空间,可以准确的知道应用程序所需要的内存。

五、程序设计与实现

1、理解程序中需要用到的系统函数和结构体

1.1 系统函数

(1)、获得当前系统的一些特征信息GetSystemInfo()函数

函数格式:VOID GetSystemInfo(LPSYSTEM_INFOlpSystemInfo);

参数:lpSystemInfo为指向SYSTEM_INFO()结构体的指针,该结构由此函数填充。

返回值:该函数没有返回值。

(2)、检查进程虚拟内存的当前信息VirtualQueryEx()函数

函数格式:

DWORD VirtualQueryEx(

HANDLE hProcess,       //进程句柄

LPCVOID lpAddress,    //指向要查询的页基地址指针

PMEMORY_BASIC_INFORMATION lpBuffer, //用以接收要查询的内存信息

//指向包含MEMORY_BASIC_INFORMATION结构的缓冲区指针

              SIZE_TdwLength  //MEMORY_BASIC_INFORMATION结构的大小

);

返回值:如果函数调用成功,则返回写入结构lpBuffer的字节数。

(3)、在调用进程的虚拟地址空间保留或提交一部分页VirtualAlloc()函数

函数格式:

LPVOID VirtualAlloc(

LPVOID lpAddress,      //待分配区域的起始地址

SIZE_T dwSize,     //要分配或者保留的区域的大小

DWORDflAllocationType,   //定义分配区域的类型属性

DWORD flProtect  //指定分配区域保护属性

);

返回值:如果函数调用成功,返回值为所分配页面的基地址;如果函数失败,返回值为NULL。

(4)、获得当前系统的存储器使用情况GetPerformanceInfo()函数

函数格式:

BOOL WINAPI GetPerformanceInfo(

PPERFORMANCE_INFORMATIONpPerformanceInformation,     

// pPerformanceInformation为指向PERFORMANCE_INFORMATION结构体的指针

DWORD cb    //PERFORMANCE_INFORMATION结构体的大小

);

返回值:如果函数调用成功,返回值为TRUE,失败返回值为FALSE。

(5)、获取系统当前物理内存和虚拟内存的使用情况GlobalMemoryStatusEx()函数

函数格式:

BOOL WINAPI GlobalMemoryStatusEx(LPMEMORYSTATUSEXlpBuffer);

//lpBuffer为指向MEMORYSTATUSEX结构体的指针。

返回值:如果函数调用成功,返回非0,失败返回0.

(6)、将数字转换成字符串StrFormatByteSize()函数

函数格式:

LPTSTR StrFormatByteSize(

LONGLONG  qdw,            //要转变的数字值

LPTSTR      pszBuf,        //指向保存将数字转变为字符串的缓冲区指针

UINT        uiBufSize      //缓冲区的容量

);

返回值:如果函数调用成功,则返回一字符串地址指针。

(7)、获取当前进程已加载模块的文件的完整路径GetModuleFileName()函数

函数格式:

DWORD WINAPI GetModuleFileName(

HMODULE  hModule,        //模块句柄

LPTSTR     lpFilename,    //存放文件路径名的字符缓冲区

DWORD     nSize            //缓冲区的大小

);

返回值:如果函数调用成功,返回复制到lpFilename的实际字符数量,如果失败返回值为0。

(8)、去掉完整路径名的路径部分PathStripPath()函数

函数格式:

VOID PathStripPath(LPTSTR pszPath);      //参数为完整的路径名

返回值:无返回值。

(9)、……

1.2 结构体

(1)、SYSTEM_INFO结构定义如下:

typedef struct _SYSTEM_INFO

{

       union{

DWORD dwOemId;

struct {

WORD wProcessorArchitecture;

WORD wReserved;

};

};

DWORD  dwPageSize; //内存页的大小

LPVOID  lpMinimumApplicationAddress; //每个进程可用地址空间的最小内存地址

LPVOID  lpMaximumApplicationAddress; //每个进程私有地址空间的最大内存地址

DWORD_PTR  dwActiveProcessorMask;   //系统配备的CPU掩码

DWORD  dwNumberOfProcessors;    //系统配备的CPU的数量

DWORD  dwProcessorType;       //系统配备的CPU的类型,向下兼容用

DWORD  dwAllocationGranularity;   //能够保留地址空间区域的最小单位

WORD   wProcessorLevel; //CPU的级别

WORD   wProcessorRevision;    //步进级别

}SYSTEM_INFO;

(2)、MEMORY_BASIC_INFORMATION结构定义如下:

typedef struct _MEMORY_INFORMATION

{

       PVOIDBaseAddress;     //按页对齐方式分配时,分配包含基地址的最小页号

       PVOIDAllocationBase; //APP的实际起始地址

       DWORDAllocationProtect;  //该区域初始设置的访问方式

       SIZE_TRegionSize;      //虚存区的大小

       DWORDState;      //区域的状态

       DWORDProtect;   //区域设置的访问方式

       DWORDType;      //区域的页面类型

}MEMORY_BASIC_INFORMATION, * PMEMORY_BASIC_INFORMATION;

注意:

①、AllocationProtect可能有的方式为PAGE_READONLY, PAGE_READWRITE,

PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE,

PAGE_EXECUTE_WRITECOPY(对于该地址空间的区域,不管执行什么操作,都不会引发访问违规。如果试图在该页上进行写入操作,就会将它自己的私有页复制赋予该进程),

PAGE_NOACCESS(禁止一切访问), PAGE_NOCACHE(禁用已提交页的高速缓存)。

②、State的状态有空闲、预留和提交(MEM_FREE, MEM_RESERVE,MEM_COMMIT)。

③、Type有可执行映像(MEM_IMAGE, 如EXE或DLL文件)、内存映射文件(MEM_MAPPING)或私有内存区(MEM_PRIVATE)。这些相邻页面拥有相同的保护属性、状态和类型。

(3)、PERFORMANCE_INFORMATION结构定义如下:

typedef struct _PERFORMANCE_INFORMATION

{

       DWORDcb;   //按字节算的结构体大小

       SIZE_TCommitTotal;   //系统当前提交的页面总数

       SIZE_TCommitLimit;  //系统当前可提交的最大页面总数

       SIZE_TCommitPeak;    //系统历史提交的页面峰值

       SIZE_TPhysicalTotal;   //按页分配的总物理内存

       SIZE_TPhysicalAvailable;    //当前可用的物理内存

       SIZE_TSystemCache;   //系统cache容量

       SIZE_TKernelTotal;     //按页算的内存容量

       SIZE_TKernelPaged;    //分页池大小

       SIZE_TPageSize;  //页的大小

       DWORDHandleCount;  //打开的句柄数

       DWORDProcessCount; //打开的进程个数

       DWORDThreadCount;  //打开的线程个数

} PERFORMANCE_INFORMATION, *PERFORMANCE_INFORMATION;

(4)、MEMORYSTATUSEX结构定义如下:

typedef struct _MEMORYSTATUSEX

{

       DWORDdwLength;      //结构体的大小

       DWORDdwMemoryLoad;    //物理内存的使用率

       DWORDLONGullTotalPhys;       //总的物理内存

       DWORDLONGullAvailPhys;      //可用的物理内存

       DWORDLONGullTotalPageFile; //总的交换文件

       DWORDLONGullAvailPageFile; //可用的交换文件

       DWORDLONGullTotalVirtual;    //总的虚拟内存

       DWORDLONGullAvailVirtual;   //可用的虚拟内存

       DWORDLONGullAvailExtendedVirtual;    //扩展内存

}MEMORYSTATUSEX, *LPMEMORYSTATUSEX;

2、编写程序

(1)、调用GlobalMemoryStatusEx()输出内存信息;

(2)、调用GetSystemInfo()输出系统信息;

(3)、调用GetPerformanceInfo()输出系统的存储器使用情况信息;

(4)、获取各个进程的信息;

(5)、对于选定的进程,利用VirtualQueryEx()检测进程的虚拟地址空间信息。

六、实验结果


                                         ……

七、实验分析与总结

通过理解Windows 的相关API,然后在程序中调用它,可以很顺利的得到上面的结果。对于本实验,自己掌握了许多关于Windows的内存信息系统函数,如GetSystemInfo(), VirtualQueryEx(), VirtualAlloc(),GetPerformanceInfo(),GlobalMemoryStatusEx()等,获取系统以及进程特征信息的函数有了更深入的了解,如各个函数的参数的意义和应用。通过此次实验也解除了我自己的一个疑惑,明白进程占用的存储空间远没有我想的那么多。在Windows平台上,每个进程的虚拟内存地址会映射为物理内存地址,Windows通过分配RAM、页面文件或两者中的空间,准确的知道APP所需要的内存。

八、实验源代码

// 内存监视.cpp : 定义控制台应用程序的入口点。/* TITLE:设计一个内存监视器,能实时地显示当前系统中内存的使用情况,包括系统地址空间         的布局,物理内存的使用情况;能实时显示某个进程的虚拟地址空间布局和工作集信息等。   作者:野狼   时间:2017/4/5*/#include "stdafx.h"#include <cstdio>#include <cstdlib>#include <iostream> #include <windows.h>#include <psapi.h>#include <tlhelp32.h>#include <shlwapi.h>#include <iomanip>#pragma comment(lib,"Shlwapi.lib")using namespace std;//显示保护标记,该标记表示允许应用程序对内存进行访问的类型inline bool TestSet(DWORD dwTarget, DWORD dwMask){return ((dwTarget &dwMask) == dwMask);}#define SHOWMASK(dwTarget,type) if(TestSet(dwTarget,PAGE_##type)){cout << "," << #type;}void ShowProtection(DWORD dwTarget){//定义的页面保护类型SHOWMASK(dwTarget, READONLY);SHOWMASK(dwTarget, GUARD);SHOWMASK(dwTarget, NOCACHE);SHOWMASK(dwTarget, READWRITE);SHOWMASK(dwTarget, WRITECOPY);SHOWMASK(dwTarget, EXECUTE);SHOWMASK(dwTarget, EXECUTE_READ);SHOWMASK(dwTarget, EXECUTE_READWRITE);SHOWMASK(dwTarget, EXECUTE_WRITECOPY);SHOWMASK(dwTarget, NOACCESS);}//遍历整个虚拟内存,并显示各内存区属性的工作程序的方法void WalkVM(HANDLE hProcess){SYSTEM_INFO si;//系统信息结构ZeroMemory(&si, sizeof(si));//初始化GetSystemInfo(&si);//获得系统信息MEMORY_BASIC_INFORMATION mbi;//进程虚拟内存空间的基本信息结构ZeroMemory(&mbi, sizeof(mbi));//分配缓冲区,用于保存信息//循环整个应用程序地址空间LPCVOID pBlock = (LPVOID)si.lpMinimumApplicationAddress;while (pBlock < si.lpMaximumApplicationAddress){//获得下一个虚拟内存块的信息if (VirtualQueryEx(hProcess,//相关的进程pBlock,//开始位置&mbi,//缓冲区sizeof(mbi)) == sizeof(mbi))//长度的确认,如果失败返回0{//计算块的结尾及其长度LPCVOID pEnd = (PBYTE)pBlock + mbi.RegionSize;TCHAR szSize[MAX_PATH];//将数字转换成字符串StrFormatByteSize(mbi.RegionSize, szSize, MAX_PATH);//显示块地址和长度cout.fill('0');cout << hex << setw(8) << (DWORD)pBlock << "-" << hex << setw(8) << (DWORD)pEnd << (strlen(szSize) == 7 ? "(" : "(") << szSize << ")";//显示块的状态switch (mbi.State){case MEM_COMMIT:printf("已提交");break;case MEM_FREE:printf("空闲");break;case MEM_RESERVE:printf("已预留");break;}//显示保护if (mbi.Protect == 0 && mbi.State != MEM_FREE){mbi.Protect = PAGE_READONLY;}ShowProtection(mbi.Protect);//显示类型switch (mbi.Type){case MEM_IMAGE:printf(", Image");break;case MEM_MAPPED:printf(", Mapped");break;case MEM_PRIVATE:printf(", Private");break;}//检验可执行的映像TCHAR szFilename[MAX_PATH];if (GetModuleFileName((HMODULE)pBlock,//实际虚拟内存的模块句柄szFilename,//完全指定的文件名称MAX_PATH) > 0)//实际使用的缓冲区长度{//除去路径并显示PathStripPath(szFilename);printf(", Module:%s", szFilename);}printf("\n");//移动块指针以获得下一个块pBlock = pEnd;}}}int main(int argc, char* argv[]){MEMORYSTATUSEX statex;//statex.dwLength = sizeof(statex);//获取系统内存信息GlobalMemoryStatusEx(&statex);printf("-----------------------内存信息-----------------------\n");//内存使用率printf("物理内存的使用率为:%ld%%\n", statex.dwMemoryLoad);//物理内存printf("物理内存的总容量为: %.2fGB.\n", (float)statex.ullTotalPhys / 1024 / 1024 / 1024);//可用物理内存printf("可用的物理内存为: %.2fGB.\n", (float)statex.ullAvailPhys / 1024 / 1024 / 1024);//提交的内存限制printf("总的交换文件为:%.2fGB.\n", (float)statex.ullTotalPageFile / 1024 / 1024 / 1024);//当前进程可以提交的最大内存量printf("可用的交换文件为:%.2fGB.\n", (float)statex.ullAvailPageFile / 1024 / 1024 / 1024);//虚拟内存printf("虚拟内存的总容量为:%.2fGB.\n", (float)statex.ullTotalVirtual/1024 / 1024 / 1024);//可用虚拟内存printf("可用的虚拟内存为:%.2fGB.\n", (float)statex.ullAvailVirtual/1024 / 1024 / 1024);//保留字段printf("保留字段的容量为:%.2fByte.\n",statex.ullAvailExtendedVirtual);printf("------------------------------------------------------\n");SYSTEM_INFO si;//系统信息结构ZeroMemory(&si, sizeof(si));GetSystemInfo(&si);//获得系统信息printf("---------------------系统信息-------------------------\n");printf("内存页的大小为:%dKB.\n", (int)si.dwPageSize/1024);cout << "每个进程可用地址空间的最小内存地址为: 0x" << si.lpMinimumApplicationAddress << endl;cout << "每个进程可用的私有地址空间的最大内存地址为: 0x" << si.lpMaximumApplicationAddress << endl;cout << "能够保留地址空间区域的最小单位为: " << si.dwAllocationGranularity/1024 << "KB" << endl;printf("------------------------------------------------------\n");//获取系统的存储器使用情况PERFORMANCE_INFORMATION pi;pi.cb = sizeof(pi);GetPerformanceInfo(&pi, sizeof(pi));printf("----------------系统的存储器使用情况------------------\n");cout << "结构体的大小为: " << pi.cb << "B" << endl;cout << "系统当前提交的页面总数: " << pi.CommitTotal << endl;cout << "系统当前可提交的最大页面总数: " << pi.CommitLimit << endl;cout << "系统历史提交页面峰值: " << pi.CommitPeak << endl;cout << "按页分配的总物理内存: " << pi.PhysicalTotal << endl;cout << "当前可用的物理内存为: " << pi.PhysicalAvailable << endl;cout << "系统Cache的容量为: " << pi.SystemCache << endl;cout << "内存总量(按页)为: " << pi.KernelTotal << endl;cout << "分页池的大小为: " << pi.KernelPaged << endl;cout << "非分页池的大小为: " << pi.KernelNonpaged << endl;cout << "页的大小为: " << pi.PageSize << endl;cout << "打开的句柄个数为: " << pi.HandleCount << endl;cout << "进程个数为: " << pi.ProcessCount << endl;cout << "线程个数为: " << pi.ThreadCount << endl;printf("------------------------------------------------------\n");//获得每个进程的信息printf("------------------各个进程的信息----------------------\n");PROCESSENTRY32 pe;pe.dwSize = sizeof(pe);HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);BOOL bMore = ::Process32First(hProcessSnap, &pe);while (bMore){HANDLE hP = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);PROCESS_MEMORY_COUNTERS pmc;ZeroMemory(&pmc, sizeof(pmc));if (GetProcessMemoryInfo(hP, &pmc, sizeof(pmc)) == TRUE){cout << " 进程ID:";wcout << pe.th32ProcessID << endl;cout << " 进程名称:";wcout << pe.szExeFile << endl;cout << " 虚拟内存的大小为:" << (float)pmc.WorkingSetSize / 1024 << "KB" << endl;}bMore = ::Process32Next(hProcessSnap, &pe);}printf("----------------------------------------------------\n");printf("-------进程虚拟地址空间布局和工作集信息查询---------\n");printf("输入要查询的进程ID:");int x;cin >> x;HANDLE hP = OpenProcess(PROCESS_ALL_ACCESS, FALSE, x);WalkVM(hP);getchar();getchar();    return 0;}

原创粉丝点击