内存监视

内存监视

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处理器上多为2GB
  • ullAvailVirtual:当前调用进程的虚拟地址空间的用户模式中未保留和未提交的内存量(单位:字节)
  • 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:系统当前提交的页数,提交页面(使用VirtualAllocMEM_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:进程标识符PID
  • cntThreads:进程启动的线程数
  • th32ParentProcessID:创建此进程的进程标识符,即父进程的PID
  • pcPriClassBase:此进程创建的线程的基本优先级
  • 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保存有关进程虚拟地址空间的一系列页面的信息,提供给VirtualQueryVirtualQueryEx使用

语法如下:

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(包含于Windows.h中)

4.3 API介绍

检索内存使用情况函数

Windows使用GlobalMemoryStatusEx函数检索有关系统物理和虚拟内存当前使用情况的信息

语法如下:

BOOL GlobalMemoryStatusEx(
  LPMEMORYSTATUSEX lpBuffer
);

说明:

  • lpBuffer:指向MEMORYSTATUSEX结构的指针,该结构接受有关当前内存可用性的信息
  • 返回值
    • 若函数成功,则返回值非零
    • 若函数失败,则返回值为0
  • 头文件:sysinfoapi.h(包括在Windows.h中)
  • DLL:KERNEL32.DLL

获取系统信息函数

Windows使用GetSystemInfo函数获取当前系统的信息

语法如下:

void GetSystemInfo(
  LPSYSTEM_INFO lpSystemInfo
);

说明:

  • lpSystemInfo:指向接受信息的SYSTEM_INFO结构体指针
  • 头文件:sysinfoapi.h(包括在Windows.h中)
  • DLL:KERNEL32.DLL

获取性能函数

Windows使用GetPerformanceInfo函数获取性能信息,填充PERFORMANCE_INFORMATION结构中的性能值

语法如下:

BOOL GetPerformanceInfo(
  PPERFORMANCE_INFORMATION pPerformanceInformation,
  DWORD                    cb
);

说明:

  • pPerformanceInformation:指向PERFORMANCE_INFORMATION结构的指针
  • cbPERFORMANCE_INFORMATION结构的大小,(单位:字节)
  • 返回值:
    • 函数成功,则返回TRUE
    • 函数失败,则返回FALSE
  • 头文件:psapi.h
  • DLL:PSAPI_VERSION=1时,使用Psapi.dll,否则使用Kernel32.dll

获取系统快照

Windows使用CreateToolhelp32Snapshot函数获取指定进程的快照,以及这些进程使用的堆、模块和线程

语法如下:

HANDLE CreateToolhelp32Snapshot(
  DWORD dwFlags,
  DWORD th32ProcessID
);

说明:

  • dwFlags:包含在快照中的系统部分,本实验选用的是TH32CS_SNAPPROCESS,表示包括快照中的所有进程,可以使用Process32First枚举这些进程
  • th32ProcessID:要包含在快照中的进程的标识符,本实验该参数为0,指示当前进程
  • 返回值:
    • 若函数成功,则返回快照句柄
    • 若函数失败,则返回INVALID_HANDLE_VALUE
  • 头文件:tlhelp32.h
  • DLL:KERNEL32.DLL

获取系统快照中第一个进程

Windows使用Process32First函数获取有关系统快照中遇到的第一额进程的信息

语法如下:

BOOL Process32First(
  HANDLE           hSnapshot,
  LPPROCESSENTRY32 lppe
);

说明:

  • hSnapshot:从先前调用的CreateToolhelp32Snapshot函数返回的快照句柄
  • ppe:指向PROCESSENTRY32结构的指针
  • 返回值:若进程列表第一个条目已经复制到缓冲区,则返回TRUE,否则返回FALSE
  • 注意:调用函数之前,必须将PROCESSENTRY32dwSize设置为结构大小,否则会失败
  • 头文件:tlhelp32.h
  • DLL:KERNEL32.DLL

获取快照中下一个进程

Windows使用Process32Next函数获取有关系统快照中记录的下一个进程的信息

语法如下:

BOOL Process32Next(
  HANDLE           hSnapshot,
  LPPROCESSENTRY32 lppe
);

说明:

  • hSnapshot:从先前调用的CreateToolhelp32Snapshot函数返回的快照句柄
  • lppe:指向PROCESSENTRY32结构的指针
  • 头文件:tlhelp32.h
  • DLL:KERNEL32.DLL

打开进程函数

Windows使用OpenProcess函数打开现有的本地进程对象

语法如下:

HANDLE OpenProcess(
  DWORD dwDesiredAccess,
  BOOL  bInheritHandle,
  DWORD dwProcessId
);

说明:

  • dwDesiredAccess:对进程对象的访问权限
  • binheritHandle:若此值为TRUE,则该进程创建的进程将继承该句柄。否则,子进程不会继承该句柄
  • dwProcessId:要打开的本地进程的标识符,在实验中取PROCESSENTRY32结构中的th32ProcessID
  • 返回值:
    • 若函数成功,则返回指定进程的打开句柄
    • 若函数失败,则返回NULL
  • 头文件:processthreadspai.h(包含于Windows.h中)
  • DLL:KERNEL32.DLL

获取进程内存使用情况

Windows使用GetProcessMemoryInfo函数获取指定进程的内存使用情况的信息

语法如下:

BOOL GetProcessMemoryInfo(
  HANDLE                   Process,
  PPROCESS_MEMORY_COUNTERS ppsmemCounters,
  DWORD                    cb
);

说明:

  • Process:进程的句柄
  • ppsemCounters:指向PROCESS_MEMORY_COUNTERSPROCESS_MEMORY_COUNTERS_EX结构的指针
  • cbppsemCounters结构大小
  • 返回值:
    • 若函数成功,则返回值非零
    • 若函数失败,则返回值为零
  • 头文件:psapi.h
  • DLL:若PSAPI_VERSION=1,则使用Psapi.dll,否则使用Kernel32.dll

显示页面保护

该段程序不是Windows提供的API,而是自己定义的若干函数

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_READ);
    SHOWMASK(dwTarget,EXECUTE);
    SHOWMASK(dwTarget,EXECUTE_READWRITE);
    SHOWMASK(dwTarget,EXECUTE_WRITECOPY);
    SHOWMASK(dwTarget,NOACCESS);
}

4.4 程序代码

本实验源代码包含在文件MemoryWatch.cpp中,如下:

#include
#include
#include
#include
#include
#include"Psapi.h"
#include"tlhelp32.h"
#include"shlwapi.h"

#define DIV_GB (1024*1024*1024)
#define DIV_KB (1024)
using namespace std;

void memoryInfo(){
    MEMORYSTATUSEX statex;
    statex.dwLength=sizeof(statex);
    GlobalMemoryStatusEx(&statex);
    printf("THE SYSTEM MEMORY INFORMATION FLOWING:\n\n");
    printf("The usage of memory is %ld%%\n",statex.dwMemoryLoad);
    printf("The total capacity of memory is %.2fGB\n",(float)statex.ullTotalPhys/DIV_GB);
    printf("The available memory is %.2fGB\n",(float)statex.ullAvailPhys/DIV_GB);
    printf("The total pages file is %.2fGB\n",(float)statex.ullTotalPageFile/DIV_GB);
    printf("The available pages file is %.2fGB\n",(float)statex.ullAvailPageFile/DIV_GB);
    printf("The total virtual space is %.2fGB\n",(float)statex.ullTotalVirtual/DIV_GB);
    printf("The available virtual space is %.2fGB\n",(float)statex.ullAvailVirtual/DIV_GB);
    printf("The available extended virtual space is %.2fGB\n",(float)statex.ullAvailExtendedVirtual/DIV_GB);
    printf("\n\n");
}

void systemInfo(){
    SYSTEM_INFO si;
    ZeroMemory(&si,sizeof(si));
    GetSystemInfo(&si);
    printf("THE SYSTEM INFORMATION FLOWING:\n\n");
    printf("The page size and the granularity of page protection and commitment is %dKB\n",(int)si.dwPageSize/DIV_KB);
    printf("The pointer to the lowest memory address accessible to applications and dynamic-link libraries (DLLs) is 0x%.8x\n",si.lpMinimumApplicationAddress);//%.8x控制长度
    printf("The pointer to the highest memory address accessible to applications and DLLs is 0x%x\n",si.lpMaximumApplicationAddress);
    printf("The number of logical processors in the current group is %d\n",si.dwNumberOfProcessors);
    printf("The granularity for the starting address at which virtual memory can be allocated is %dKB\n",si.dwAllocationGranularity/DIV_KB);
    printf("\n\n");
}

void performanceInfo(){
    PERFORMANCE_INFORMATION pi;
    pi.cb=sizeof(pi);
    GetPerformanceInfo(&pi, sizeof(pi));
    printf("THE PERFORMANCE INFORMATION IS FOLLOWING:\n\n");
    printf("The number of pages currently committed by the system is %d\n",pi.CommitTotal);
    printf("The current maximum number of pages that can be committed by the system without extending the paging file(s) is %d\n",pi.CommitLimit);
    printf("The maximum number of pages that were simultaneously in the committed state since the last system reboot is %d\n",pi.CommitPeak);
    printf("The amount of actual physical memory in pages is %d\n",pi.PhysicalTotal);
    printf("The amount of physical memory currently available is %d\n",pi.PhysicalAvailable);
    printf("The amount of system cache memory is %d\n",pi.SystemCache);
    printf("The sum of the memory currently in the paged and nonpaged kernel pools is %d\n",pi.KernelTotal);
    printf("The memory currently in the nonpaged kernel pool is %d\n",pi.KernelNonpaged);
    printf("The size of a page is %dKB",pi.PageSize/DIV_KB);
    printf("The current number of open handles is %d\n",pi.HandleCount);
    printf("The current number of processes is %d\n",pi.ProcessCount);
    printf("The current number of threads is %d\n",pi.ThreadCount);
    printf("\n\n");
}

void processInfo(){
    PROCESSENTRY32 pe;
    pe.dwSize=sizeof(pe);
    HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    BOOL bMore = ::Process32First(hProcessSnap, &pe);
    printf("THE ALL PROCESS INFORMATION IS FOLLOWING:\n\n");
    printf("PID\t | Execute File\t\t\t\t\t | Working Set Size(KB)\n");
    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))){
            int len=strlen(pe.szExeFile);
            printf("%d\t | %s",pe.th32ProcessID,pe.szExeFile);t
            for(int i=0;i<=45-len;i++){
                printf(" ");//控制长度,打表好看
            }
            printf("| %.2f\n",(float)pmc.WorkingSetSize/DIV_KB);
        }
        bMore = ::Process32Next(hProcessSnap, &pe);
    }
    CloseHandle(hProcessSnap);
}

//以下三个功能函数来自书本第292页

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_READ);
    SHOWMASK(dwTarget,EXECUTE);
    SHOWMASK(dwTarget,EXECUTE_READWRITE);
    SHOWMASK(dwTarget,EXECUTE_WRITECOPY);
    SHOWMASK(dwTarget,NOACCESS);
}

void processVirtualMemoryInfo(){
    int pid;
    printf("Please enter the pid that you want to search\n"); 
    SYSTEM_INFO si;
    ZeroMemory(&si,sizeof(si));
    GetSystemInfo(&si);
    HANDLE hProcess;
    while(TRUE){
        printf("PID:\t");
        scanf("%d",&pid);
        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
        if (hProcess==NULL) {
            printf("You Enter A INVALID Pid, Please Check and Enter again:\n");
        }
        else{
            break;
        }
    }
    MEMORY_BASIC_INFORMATION mbi;
    ZeroMemory(&mbi,sizeof(mbi));
    LPCVOID pBlock = (LPVOID)si.lpMinimumApplicationAddress;
    printf("Address\t\t\t | Size\t\t | State\t | Protect\t | Type\t\t | Module\n\n");
    while(pBlock < si.lpMaximumApplicationAddress){
        if (VirtualQueryEx(hProcess,pBlock,&mbi,sizeof(mbi))==sizeof(mbi)) {
            LPCVOID pEnd = (PBYTE)pBlock + mbi.RegionSize;
            TCHAR szSize[MAX_PATH];
            StrFormatByteSize(mbi.RegionSize, szSize, MAX_PATH);
            printf("%.8x-%.8x\t",(DWORD)pBlock,(DWORD)pEnd);
            printf("| %s\t",szSize);
            switch (mbi.State)
            {
                case MEM_COMMIT:
                    printf("| Commited\t");
                    break;
                case MEM_FREE:
                    printf("| Free\t\t");
                    break;
                case MEM_RESERVE:
                    printf("| Reserved\t");
                    break;
                default:
                    break;
            }
            if (mbi.Protect == 0 && mbi.State != MEM_FREE) {
                mbi.Protect = PAGE_READONLY;
            }
            ShowProtection(mbi.Protect);
            printf("\t");
            switch (mbi.Type)
            {
                case MEM_IMAGE:
                    printf("| Image\t");
                    break;
                case MEM_MAPPED:
                    printf("| Mapped\t");
                    break;
                case MEM_PRIVATE:
                    printf("| Private\t");
                    break;
                default:
                    break;
            }
            TCHAR szFilename[MAX_PATH];
            if (GetModuleFileName((HINSTANCE)pBlock,szFilename,MAX_PATH)>0) {
                PathStripPath(szFilename);
                printf("\t| %s",szFilename);
            }
            printf("\n");
            pBlock=pEnd;
        }
    }
}

void clear(){
    system("cls");
}

int main(int argc, char const *argv[])
{
    while(TRUE){
        printf("What do you want to know?\n");
        printf("Memory Information press: 'u'\nSystem Information press 'i'\nPerformance Information press 'o'\nProcess Infomation press 'p'\nProcess Virtual Memory Information press 'm'\nQuit press 'q'\n");
        char c=getchar();
        if(c=='q'){
            break;
        }
        else if(c=='u'){
            getchar();//去掉回车
            memoryInfo();
        }
        else if(c=='i'){
            getchar();
            systemInfo();
        }
        else if(c=='o'){
            getchar();
            performanceInfo();
        }
        else if(c=='p'){
            getchar();
            processInfo();
        }
        else if(c=='m'){
            getchar();
            processVirtualMemoryInfo();
            getchar();
        }
    }
    return 0;
}

程序运行方式如下:

g++ MemoryWatch.cpp -lPsapi -lShlwapi -o MemoryWatch.exe
MemoryWatch.exe

注意:由于实验中需要用到动态链接库,所以编译时需要添加动态链接库参数

4.5 运行结果

实验者在主要程序外嵌套了一层交互程序,运行程序可以交互进行实时内存监控,如下

4.5.1 查询内存信息

内存监视_第1张图片

4.5.2 查询系统信息

内存监视_第2张图片

4.5.3 查询性能信息

内存监视_第3张图片

4.5.4 查询进程

内存监视_第4张图片

4.5.5 查询进程虚拟地址空间

需要输入PID,若PID无效则会提示无效信息,可以通过4.5.5查询有效的PID

内存监视_第5张图片

5. 实验收获与体会

  1. 通过本实验,掌握了许多在Windows下关于内存信息的系统调用函数,了解系统内部内存的工作方式和工作情况
  2. 对获取系统信息的API有了更加深入的了解
  3. 对操作系统内存分配方式有了进一步的印象。Windows把每个进程的虚拟内存地址映射到物理内存地址,操作系统通过页式管理的方式对内存进行管理
  4. 对于本实验的所有数据结构和系统调用函数,均在微软文档中进行阅读和深入理解,实验进一步训练自己阅读文档获取函数、数据结构声明的能力。

你可能感兴趣的:(BIT)