本文是操作系统内存管理的基本实验。
一、实验目的
在本次实验中,需要从不同的侧面了解Windows 的虚拟内存机制。在Windows操作系统中,可以通过一些API 操纵虚拟内存。主要需要了解以下几方面:
- Windows 虚拟存储系统的组织
- 如何控制虚拟内存空间
- 如何编写内存追踪和显示工具
- 详细了解与内存相关的API 函数的使用
二、实验需求
编写一个包含两个线程的进程,一个线程用于模拟内存分配活动,一个线程用于跟踪第一个线程的内存行为。模拟内存活动的线程可以从一个文件中读出要进行的内存操作,每个内存操作包含如下内容:
- 时间:开始执行的时间
- 块数:分配内存的粒度
- 操作:包括保留一个区域、提交一个区域、释放一个区域、回收一个区域以及锁与解锁一个区域;可以将这些操作编号,存放于文件中
- 大小:指块的大小
- 访问权限:共五种PAGE_READONLY、PAGE_READWRITE、PAGE_EXCUTE、PAGE_EXECUTE_READ 和PAGE_EXECUTE_READWRITE。可以将这些权限编号,存放于文件中。跟踪线程将页面大小、已使用的地址范围、物理内存总量以及虚拟内存总量等信息显示出来。
三、运行环境
电脑硬件配置:
处理器:Intel i7 7700HQ
显卡:NVIDIA GeForce GTX 1050 Ti
内存:16GB
软件:
编程语言:C++
IDE:Microsoft Visual Studio 2019
Windows SDK版本:10.0
平台工具集:Visual Studio 2019(v142)
四、实验原理
内存管理是Windows执行体的一部分,位于Ntoskrnl.exe 文件中,是整个操作系统的重要组成部分。物理内存是固定的,内存条的容量多大,物理内存就有多大。但是如果程序运行很多或者程序本身很大的话,就会导致大量的物理内存占用,甚至导致物理内存消耗殆尽。
虚拟内存就是在硬盘上划分一块页面文件,充当内存。当程序在运行时,有一部分资源还没有用上或者同时打开几个程序却只操作其中一个程序时,系统没必要将程序所有的资源都放在物理内存中。于是,系统将这些暂时不用的资源放在虚拟内存上,等到需要时在调出来用。这也是虚拟内存的优点:需要的时候才真正分配内存。
默认情况下,32位Windows上每个用户进程可以占有2GB的私有地址空间,操作系统占有剩下的2GB。Windows在x86体系结构上利用二级页表结构来实现虚拟地址向物理地址的变换。一个32位虚拟地址被解释为三个独立的分量——页目录索引、页表索引和字节索引——它们用于找出描述页面映射结构的索引。页面大小及页表项的宽度决定了页目录和页表索引的宽度。比如,在x86 系统中,因为一页包含4096字节,于是字节索引被确定为12位宽(2^12=4096)。
应用程序有三种使用内存方法:
- 以页为单位的虚拟内存分配方法,适合于大型对象或结构数组
- 内存映射文件方法,适合于大型数据流文件以及多个进程之间的数据共享
- 内存堆方法,适合于大量的小型内存申请
本次实验主要是针对第一种使用方式。应用程序通过API函数 VirtualAlloc和VirtualAllocEx等实现以页为单位的虚拟内存分配方法。首先保留地址空间,然后向此地址空间提交物理页面,也可以同时实现保留和提交。保留地址空间是为线程将来使用保留一块虚拟地址。在已保留的区域中,提交页面必须指出将物理存储器提交到何处以及提交多少。提交页面在访问时会转变为物理内存中的有效页面。
五、实验步骤
1、编译运行makefile.cpp,生成opfile文件。opfile里记录了几个不同的内存操作,包括时间、块数、操作、大小、访问权限。生成了包括5个权限和6个操作任意组合共30个操作。
2、将生成的opfile复制到memory-op的工程目录下
3、编译运行memory-op.cpp,产生两个线程,一个从opfile文件里读取内存操作,模拟内存活动,另一个跟踪第一个的内存行为,将结果输出,并保存在out.txt文件中。
4、输出结果
六、相关的API 函数
点击查看代码
1 | //可以通过GetSystemInfo,GlobalMemoryStatus 和VirtualQuery 来查询进程虚空间的状态。主要的信息来源如下: |
七、项目地址
本项目的源码、可执行程序均已经存放于我的Github,欢迎下载查看: