内存监视
1. 实验目的
熟悉Windows存储器管理中提供的各类机制和实现的请求调页技术。
通过实验,了解Windows内存结构和虚拟内存管理,学习如何在应用程序中管理内存。了解当前系统中内存的使用情况,包括系统地址空间的布局,物理内存的使用情况;能够实时显示某个进程的虚拟地址空间布局和工作集信息等
2. 实验内容
在Windows平台上设计一个内存监视器,能够实时地显示当前系统中的内存使用情况,包括地址空间的布局,物理内存的使用情况,能实时显示某个进程的虚拟地址空间布局和工作集信息等
相关的系统调用:
GetSystemInfo
VirtualQueryEx
VirtualAlloc
GetPerformanceInfo
GlobalMemoryStatusEx
3. 实验环境
本实验基于本机macOS系统下的Windows虚拟机完成,具体实验环境如下:
3.1 宿主机环境
宿主机环境配置如下:
- 操作系统:macOS
- 内存容量:8GB
- 处理器:2.9GHz Intel Core i5
- 硬盘容量:500GB
3.2 虚拟机环境
Windows虚拟机环境配置如下:
- 虚拟机软件:VMware Fusion 11
- 虚拟机操作系统:Windows 7 旗舰版
- 虚拟机内存:4GB
- 虚拟机硬盘容量:60GB
4. 程序设计和实现
4.1 实验原理
内存指的是计算机配置的RAM,系统可以管理所有的物理内存。操作系统(本实验特指Windows)通过分配RAM、页面文件或者二者中的空间,可以准确获取应用程序需要的内存
在Windows下运行的每个应用程序都认为能够独占4GB的虚拟地址空间,其中,低2GB为进程私有地址空间,用于存放程序和动态链接库,高2GB为所有进程共享区,也就是操作系统占用区。
事实上,少有进程可以占有2GB存储空间。Windows把每个进程的虚拟内存地址映射为物理内存地址。
4.2 数据结构介绍
内存状态结构体
Windows中使用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
:结构体大小,需要在调用GlobalMemoryStatusEx
之前设定dwMemoryLoad
:物理内存使用的百分比(从0到100)ullTotalPhys
:实际物理内存的大小(单位:字节)ullAvailPhys
:现在可用的物理内存大小(单位:字节)这部分物理内存是可以不需要写入磁盘立即被回收的内存。它是备用,空闲和零列表大小的综合ullTotalPageFile
:系统或当前进程已经提交的内存限制中的较小者(单位:字节)。要获取系统范围的已提交内存限制需要调用GetPerformanceInfo
ullAvailPageFile
:当前进程可以提交的最大内存量(单位:字节)ullTotalVirtual
:调用进程的虚拟地址空间的用户模式部分的大小(单位:字节)。此值取决于进程类型,处理机类型和操作系统配置,在x86处理器上多为2GBullAvailVirtual
:当前调用进程的虚拟地址空间的用户模式中未保留和未提交的内存量(单位:字节)ullAvailExtendevVirtual
:保留值,始终为0- 头文件:
sysinfoapi.h
(包括在Windows.h
中)
计算机系统信息结构体
Windows使用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;
说明:(该结构体成员较多,只介绍部分)
dwPageSize
:页面大小和页面保护和提交的粒度,是VirtualAlloc
函数使用的页面大小lpMinimumApplictionAddress
:指向应用程序和动态链接库(DLL)可访问的最低内存指针lpMaximumApplicationAddress
:指向应用程序和动态链接库(DLL)可访问的最高内存指针dwActiveProcessorMask
:配置到系统中的处理器集掩码dwNumberOfProcessors
:当前组中的逻辑处理器数,若要检索,则需要调用GetLogicalProcessorInformation
函数dwAllocationGranularity
:可以分配虚拟内存的其实地址的粒度- 头文件:
sysinfoapi.h
(包含于Windows.h
中)
性能信息结构体
Windows使用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;
说明:(该结构体成员较多,只介绍部分)
cb
:结构体大小(单位:字节)CommitTotal
:系统当前提交的页数,提交页面(使用VirtualAlloc
和MEM_COMMIT
)会立即更新该值;但是在访问页面之前,物理内存不会被填充CommitPeak
:自上次系统重新引导以来同时处于已提交状态的最大页数PhysicalTotal
:实际物理内存,以页为单位PhysicalAvailable
:当前可用的物理内存量,以页为单位。这部分内存是可以立即重用无需写入磁盘的内存,是备用,空闲和零列表大小的总和SystemCache
:系统缓存内存量,以页为单位。该值为备用列表大小加上系统工作集KernelTotal
:分页和非分页内核池中当前内存的总和,以页为单位KernelPaged
:当前在分页内核池的内存,以页为单位KernelNonpaged
:当前在非分页内核池中的内存,以页为单位PageSize
:页面大小,以字节为单位HandleCount
:当前打开手柄的数量ProcessCount
:当前进程数ThreadCount
:当前线程数- 头文件:
psapi.h
进程列表条目
Windows使用PROCESSENTRY32
结构体描述在拍摄快照时驻留在系统地址空间中的进程列表中的条目
语法如下:
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
CHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
说明:
dwSize
:结构体大小,以字节为单位。在调用Process32First
函数之前要设置为sizeof(PROCESSENTRY32)
,若没有设置,则函数调用会是失败th32ProcessID
:进程标识符PIDcntThreads
:进程启动的线程数th32ParentProcessID
:创建此进程的进程标识符,即父进程的PIDpcPriClassBase
:此进程创建的线程的基本优先级szExeFile
:进程的可执行文件- 其他成员:不再使用,始终设置为0
- 头文件:
tlhelp32.h
进程内存统计信息结构
Windows使用PROCESS_MEMORY_COUNTERS
结构体保存进程的内存统计信息
语法如下:
typedef struct _PROCESS_MEMORY_COUNTERS {
DWORD cb;
DWORD PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
} PROCESS_MEMORY_COUNTERS;
说明:
cb
:结构体大小PageFaultCount
:页面错误的数量WorkingSetSize
:当前工作集大小(单位:字节)PeakWorkingSetSize
:峰值工作集大小(单位:字节)QuotaPeakPagedPoolUsage
:峰值分页池使用情况(单位:字节)QuotaPagedPoolUsage
:当前页面缓冲池使用情况(单位:字节)QuotaPeakNonPagedPoolUsage
:非页面缓冲池使用率的峰值(单位:字节)QuotaNonPagedPoolUsage
:当前非分页池使用情况(单位:字节)PagefileUsage
:此进程的Commit Charge值(单位:字节),Commit Charge是内存管理器为正在运行的进程提交的内存总量PeakPagefileUsage
:此进程的生命周期内提交的峰值(单位:字节)- 头文件:
psapi.h
虚拟地址空间信息
Windows使用MEMORY_BASIC_INFORMATION
保存有关进程虚拟地址空间的一系列页面的信息,提供给VirtualQuery
和VirtualQueryEx
使用
语法如下:
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;
说明:
BaseAddress
:指向页面区域的基址指针AllocationBase
:指向VirtualAlloc
函数分配的一系列页面的基址指针AllocationProtect
:最初分配区域时的内存保护选项RegionSize
:从基址开始的区域大小,其中所有页面都具有相同属性(单位:字节)State
:页面的状态,可以是以下值:MEM_COMMIT
:表示已在内存中或磁盘页面文件中为其分配物理存储的已提交页面MEM_FREE
:表示调用进程无法访问且可以分配的空闲页面MEM_RESERVE
:表示保留进程的虚拟地址空间范围而不分配任何物理存储的保留页面
Protect
:该区域中页面的访问保护Type
:页面的类型,可以是以下值:MEM_IMAGE
:指示区域内的内存页面映射到映射文件到视图中MEM_MAPPED
:指示区域内的内存页面映射到节的仕途MEM_PRIVATE
:指示区域内页面是私有的
- 头文件:
winnt.h
(包含于Wi