【操作系统实验】内存监视器实验

一、实验目的

独立设计并实现一个内存监视器,以加深对内存管理的理解。

二、实验内容

在Windows系统下设计实现一个内存监视器,使用该内存监视器:

  1. 能够实时显示当前系统中内存的使用情况,包括系统地址空间的布局,物理内存的使用情况;

  1. 能够实时显示某个进程的虚拟地址空间布局和工作集信息等。

三、程序设计与实现

1. 实验环境

操作系统:Windows10

开发环境:Visual Studio 2022

2.设计思路

① 获取系统信息

  • 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

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);

② 获取物理内存信息

主要使用到的数据结构和函数有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必须进行指定为 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中,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是访问该进程的方式,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)。

  1. 编写、编译代码

在Visual Studio中编写C++代码,使用动态链接库,实现相应功能。

  1. 运行得到结果

  1. 编写、编译代码

在Visual Studio中编写C++代码,使用动态链接库,实现相应功能。

  1. 运行得到结果

四、实验结果及分析

操作过程中,我们通过对问题描述的分析,完成获取系统信息、获取物理内存信息、获取所有进程的基本信息、获取单个进程的详细信息,完成了内存监视器的设计,能实时地显示当前系统中内存的使用情况,包括系统地址空间的布局,物理内存的使用情况,也能实时显示某个进程的虚拟地址空间布局和工作集信息等,实验结果见附录。

五、实验收获与体会

“内存监视”是操作系统知识里非常重要的部分,通过本次实验,我进一步加深了对内存和进程概念的理解,深刻了解到内存监视的过程。

通过本实验,我掌握了许多在Windows下关于内存信息的系统调用函数,了解系统内部内存的工作方式和工作情况,对获取系统信息的API有了更加深入的了解,同时对操作系统内存分配方式有了进一步的印象。

Windows把每个进程的虚拟内存地址映射到物理内存地址,操作系统通过页式管理的方式对内存进行管理。

在亲自动手实验的过程中,我对本章知识点有了更深刻的理解,同时也复习了程序设计方面的知识,这次实验让我收获颇多!

项目源码及实验报告:https://github.com/YourHealer/OS-Memory-Monitor.git

猜你喜欢

转载自blog.csdn.net/ayaishere_/article/details/128709498