为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

虚拟内存实验

2010-12-06 14页 doc 141KB 32阅读

用户头像

is_135837

暂无简介

举报
虚拟内存实验实习二:Windows虚拟存储器管理 实习目的 •​ 了解windows 2000/xp的内存管理机制,掌握页式虚拟存储技术 •​ 理解内存分配原理,特别是以页面为单位的虚拟内存分配方法。 •​ 掌握windows 2000/xp下内存管理的基本api •​ 同时需要了解跟踪程序的编写方法(与被跟踪程序保持同步,使用Windows提供的信号量) •​ 对Windows分配虚拟内存、改变内存状态,以及对物理内存(physical memory)和页面文件(pagefile)状态查询的API函数的功能、参数限制、使用规则要进一步了...
虚拟内存实验
实习二:Windows虚拟存储器管理 实习目的 •​ 了解windows 2000/xp的内存管理机制,掌握页式虚拟存储技术 •​ 理解内存分配原理,特别是以页面为单位的虚拟内存分配方法。 •​ 掌握windows 2000/xp下内存管理的基本api •​ 同时需要了解跟踪程序的编写方法(与被跟踪程序保持同步,使用Windows提供的信号量) •​ 对Windows分配虚拟内存、改变内存状态,以及对物理内存(physical memory)和页面文件(pagefile)状态查询的API函数的功能、参数限制、使用规则要进一步了解。 实习要求 •​ 使用Windows 2000/XP的API函数,编写一个包含两个线程的进程,一个线程用于模拟内存分配活动,一个线程用于跟踪第一个线程的内存行为,而且要求两个线程之间通过信号量实现同步。 •​ 运行结果显示要求:每次内存分配操作给出一组此次分配的相关信息,包括操作类型、权限类型、分配的起始地址和大小等;每次内存分配操作之后给出一组关于系统和内存的当前状态的信息 •​ 模拟内存活动的线程可以从一个文件中读出要进行的内存操作,每个内存操作包括如下内容 •​ 时间:操作等待时间,即等待相应时间后执行内存分配操作;要求随机产生 •​ 块数:分配内存的粒度。操作的内存页数;要求随机产生 •​ 操作:包括保留(reserve)一个区域、提交(commit)一个区域、释放(release)一个区域.回收(decommit)一个区域和加锁(lock)与解锁(unlock)一个区域,可以将这些操作编号存放于文件。 •​ 保留:在虚拟地址空间分配,不分配物理空间 •​ 提交:在物理地址空间分配 •​ 回收:释放物理空间,但保留虚拟空间(与提交相对应,即可以回收已经提交的内存块) •​ 释放:释放物理空间和虚拟空间(释放是指将物理存储和虚拟地址空间全部释放,它与保留(reserve)相对应,即可以释放已经保留的内存块) •​ 加锁:常驻内存,即防止操作系统把对应的内存空间换出到外存 •​ 解锁:允许操作系统把对应的内存空间换出到外存它。 •​ 大小:块的大小。 •​ 访问权限:共五种,分别为PAGE_READONLY,PAGE_READWRITE,PAGE_EXECUTE,PAGE_EXECUTE_READ和PAGE EXECUTE_READWRITE。可以将这些权限编号存放于文件中 •​ 跟踪线程将页面划大小、已使用的地址范围、物理内存总量,以及虚拟内存总量等信息显示出来 •​ 等待时间 块大小(页面数) 操作类型 保护类型 •​ 如:8 12 COM_COMMIT PAGE_READONLY等待8秒后,向物理及虚拟内存申请分配一个12个页面大小的,只读的空间。 •​ 等待时间及块大小都是整数,可以使用VC内嵌函数rand()函数随机产生。 •​  相关基础知识 •​ Windows中的虚拟存储技术(分页) Windows在实现虚拟存储技术的时候,利用页面文件(paging file)来实现物理内存的扩展。 所谓的页面文件就是Windows 2000/XP在硬盘上分配的用来存储没有装入内存的程序和数据文件部分的磁盘文件。这个文件是一个名叫pagefile.sys的系统隐藏文件,当系统安装时,会在安装系统盘的根目录下创建该文件,其默认值大于计算机中RAM的1.5倍 需要时Windows2000/XP将数据从页面文件移至内存,并将数据从内存移至页面文件以便为新数据释放空间。页面文件也称为交换文件。 •​ 页面文件和物理内存或RAM构成“虚拟内存”。如果系统要求的内存量超过了虚拟内存的大小,则系统就会出现提示,发出虚拟内存不足的警告。 •​ 我们可以根据需要设置虚拟内存的大小,方法是:右击“我的电脑”,依次选择“属性” - >“高级”->“性能选项”->“更改”;在这个设置功能下,我们还可以在其他分区或者磁盘下新增页面文件(默认情况下,非系统盘上没有设置页面文件),这样相应的磁盘根目录下也会出现一个系统隐藏文件 Windows 2000/XP在X86体系结构上利用二级页表结构来实现虚拟地址向物理地址的变换。一个32位虚拟地址被解释为三个独立的分量——页目录索引、页表索引和字节索引——它们用于找出描述页面映射结构的索引。页面大小及页表项的宽度决定了页目录和页表索引的宽度。 虚拟地址变换基本步骤: (1)内存管理的硬件设备定位当前进程的页目录。每次进程切换时,通过CPU寄存器来通知页目录的所在地址。 (2)页目录索引用于在页目录中指出页目录项的位置。页目录项描述了映射页表的位置。 (3)页表索引用于在页表中指明页表项的位置。页表项描述了虚拟页面在物理内存的位置。如果页表项表明所需的页是无效的,内存管理器的故障处理程序会定位该页,并试图使之有效。 (4)当页表项指向了有效的页时,字节索引用于找到物理页内所需数据的地址。 Windows2000/XP的内存保护机: (1) 核心态组件使用的数据结构和内存缓冲池(核心态和用户态)。 (2)每个进程有一个独立的私有的地址空间。惟一例外是,该进程和其他进程共享页面,或另一进程具有对进程对象的虚拟内存读写权限,可以使用 ReadProcessMemory或WriteProcessMemory函数。 (3) 硬件内存保护措施(如读/写,只读等)。。例如,在进程的地址空间中代码页被标志为只读,可以防止被用户线程修改。 (4)共享内存区域对象具有标准的Windows 2000/XP存取控制表(ACL),,这样对内存的访问也被限制在具有适当权限的进程之中 Windows2000/XP进程地址空间布局 范围 大小 功能 0x0到0xFFFF 64KB 拒绝访问区域,用于帮助程序员避免引用错误的指针;试图访问这个区域地址的操作将会导致访问侵犯。 0x10000到 0x7FFEFFFF 2GB减掉至少192KB 专用进程地址空间 0x7EFDE000到 0x7EFDEFFF 4KB 用于第1个线程的线程环境块(TEB)。系统会在这一页的前面创建附加的TEB(从地址0x7FFDD000开始向上)。 0x7FFDF000到 0x7FFDFFFF 4KB 进程环境块(PEB) 0x7FFE0000到 0x7FFE0FFFF 4KB 共享的用户数据页,这个只读方式的页面被映射到系统空间中包含系统时间、时钟计数和版本号信息的一个页面。这个页面的存在使数据在用户态下可以直接读取而不必请求核心态转换。 0x7FFE1000到 0x7FFEFFFF 64KB 拒绝访问区域(共享用户数据页面以后剩余的64KB)。 0x7FFF0000到 0x7FFFFFFF 64KB 拒绝访问区域,用于防止线程跨越用户/系统空间边界传送缓存区。在MmUserProbeAddress中包含此页的起始地址。 范围 大小 功能 0x0到0xFFFF 64KB 拒绝访问区域,用于帮助程序员避免引用错误的指针;试图访问这个区域地址的操作将会导致访问侵犯。 0x10000到 0x7FFEFFFF 2GB减掉至少192KB 专用进程地址空间 0x7EFDE000到 0x7EFDEFFF 4KB 用于第1个线程的线程环境块(TEB)。系统会在这一页的前面创建附加的TEB(从地址0x7FFDD000开始向上)。 0x7FFDF000到 0x7FFDFFFF 4KB 进程环境块(PEB) 0x7FFE0000到 0x7FFE0FFFF 4KB 共享的用户数据页,这个只读方式的页面被映射到系统空间中包含系统时间、时钟计数和版本号信息的一个页面。这个页面的存在使数据在用户态下可以直接读取而不必请求核心态转换。 0x7FFE1000到 0x7FFEFFFF 64KB 拒绝访问区域(共享用户数据页面以后剩余的64KB)。 0x7FFF0000到 0x7FFFFFFF 64KB 拒绝访问区域,用于防止线程跨越用户/系统空间边界传送缓存区。在MmUserProbeAddress中包含此页的起始地址。 Windows 2000/XP内存管理Ntoskrnl.exe文件 (1)一组执行体系统服务程序。用来分配、回收和管理虚拟内存。 (2)一个转换无效和访问错误陷阱处理程序。用于解决硬件检测到的内存管理异常,并代表进程将虚拟页面装入内存。 (3)运行在核心态系统线程的6个组件。 ①工作集管理器。优先级为16 ②进程/堆栈交换程序。优先级为23,完成进程和内核线程堆栈的换入和换出操作。 ⑧已修改页面写入器。优先级为17,将修改链表上的脏页写回到适当的页文件。 ④映射页面写入器。优先级为17,将映射文件中的脏页写回磁盘。 ⑤废弃段线程。优先级为18,负责系统高速缓存和页面文件的扩大和缩小。 ⑥零页线程。优先级为0,将空闲链表中的页面清零,以便有足够的零页面满足将来的零页需求 存储系统的统计指标 系统中维护结构体MEMORYSTATUS,我们可以通过这个结构体来看系统的虚拟和物理内存的指标。 •​ dwlength:指明本结构所占的空间大小,在使用适当的函数GlobalMemoryStatus从系统中获取这个结构的数据时,该系统函数会给这个域设置正确的值。 •​ dwMemoryload:物理存储使用负荷指数,使用一个百分数表示当前物理内存已经被占用的比率。在利用Win 32API查询得到的此结构中,这个比率只是精确到个位,而且是选择进一的原则,例如使用了78.2%的物理内存,则显示占用79%。 •​ dwTotalPhys:系统中安装的物理内存总数,以Byte(字节)计数。 •​ dwAvailPhys:可用物理内存数,以Byte计数。 •​ dwTotalPagefile:页面文件总数,也就是系统在外存上为虚拟内存系统分配的页面文件(pagingfile)的总量,以Byte计数 •​ dwAvailPagefile:可用页面文件数,以Byte计数。 •​ dwTotalVirtual:本进程中用户可以访问的虚存空间总数。注意目前32位Windows系统中,在总共4GB的空间中,高端的2GB是系统占用的,只有低端的2GB才是用户可以访问的。此处以Byte计数,也就是说在Windows2000/XP该数字应该显示为2147352576。 •​ dwAvailVirtual:在本进程中用户可以访问虚存空间中可用部分的数量,也就是还没有被程序分配的用户虚拟空间大小,以Byte计数。 程序的结构 •​ 1 Makefile.cpp 本程序主要实现将操作写入文件,采用了C语言的fwrite函数直接以结构(struct)为单位写入 文件。用了两层循环,外层循环控制对内存的操作(保留、提交、锁、解锁、回收、释放),内层循环控制对内存操作的权限(PAGE_READONLY只读、PAGE_READWRITE读写、PAGE_EXECUTE执行、PAGE_EXECUTE_READ执行和读、PAGE_EXECUTE_READWRITE执行和读写)用随机数生成等待执行的时间和分配的粒度。 •​ 2.Memory-op.cpp (1)主函数 创建两个线程,并将返回的句柄存人数组中。 创建两个信号量(allo,trac)分别用于通知跟踪线程和记录线程。 用函数WaitForMultipleObjects来等待两个线程的结束。 (2)Tracker线程(记录内存的状况) a)打开文件,准备输出。 b)等待线程Allocator的一次内存操作完毕(即等待信号量trac的释放)。 c)用函数Getsystemlnfo得到系统信息(该信息不随内存分配的变化而变化)。 d)用函数GlobalMemoryStatus得到内存信息(随内存的分配各项信息会有所改变)。用函数 VirtualQuery得到虚拟内存基本信息(该信息不随内存分配的变化而变化)。 e)释放信号量,通知Allocator线程可以进行下一次内存分配活动。 f)如果已经记录了所有的内存分配信息,线程退出,否则转到b)。 (3)Allocator线程(模拟内存分配活动) a)打开文件(makefile.cpp的输出结果),准备读入。 b)等待Tracker输出的结束(即等待信号量allo的释放)。 c)读文件(makefile.cpp的输出结果)。 d)根据文件内容(protection)确定对内存操作时的权限。 e)根据文件内容(oper)确定对内存的具体操作。 f)释放信号量(trac)通知Tracker线程可以进行一次输出。 g)如果文件中所有的分配信息已经完成,线程退出,否则转到b)。 相关API函数说明 •​ 1.GetSystemlnfo •​ 函数功能: •​ 该函数返回当前系统的信息。 •​ 该函数可以获得计算机系统中当前使用的物理内存和虚拟内存的信息。 •​ 2.GlobalMemoryStatus函数 功能:用于获取程序存储空间的使用状况以及系统的使用概况。 VOID MemoryStatus (LPMEMORYSTATUS lpBuffer); 参数说明:这个参数 lpBuffer指向MEMORYSTATUS结构的一个指针,用于函数返回相关信息。 •​ 3.VirtualQuery(可不用) •​ 函数功能: •​ 该函数提供有关调用进程虚拟空间中的页面信息。查询一个进程的虚拟内存。给定一个进程的地址空进为2GB,如果没有查询地址信息的能力,管理地址的全部范围将是困难的。查询就是要读取保存虚拟空间状态的数据结构。 •​  4.virtualAlloc •​  函数功能: •​  该函数保留或提交某一范围的虚拟地址。当在一个进程中保留一段虚拟地址时,并没有物理内存页被提交。而且,保留一个地址范围将不会保证将来有可用的物理内存来提交给这些地址。要使用保留地址,内存首先被提交给该地址,内存物理页被分配。 •​ 函数原型:LPVOID VirtualAlloc (LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect); •​ 函数参数: •​ lpAddress:待分配空间的起始地址。若指定的内存被保留,指定的地址将四舍五入到下一个64K边界;若指定的内存被保留且被提交,指定的地址将四舍五入到下一个页面边界。若为NULL,则由系统决定分配区域的地址。 •​ dwSize:分配空间的大小(以字节为单位)。 •​ flAllocationType:MEM_COMMIT或MEM_RESERVE或两者的组合。 •​ flProtect:存取保护的类型(参考测试数据的说明部分)。 •​ 返回值:调用成功则返回所分配页面的基址,否则返回NULL。 •​ 5.VirtualFree •​ 函数功能:解除已被提交的虚存或者释放被保留或者提交的进程虚拟地址空间。 •​ 将保留或提交的地址页面恢复自由地址状态的方法。还可以对已提交的虚拟地址页解除提交,使得该段地址空间变成保留状态。解除提交时,相关物理内存都会被释放 •​ BOOL VirtualFree (LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType); •​ 函数参数: •​ lpAddress:待释放空间的起始地址。若dwFreeType参数中包含MEM_RELEASE标志,当该页面被保留时,这个参数必须是通过VirtualAlloc函数返回的基址。 •​ dwSize:释放空间的大小(以字节为单位)。若dwFreeType参数中包含MEM_RELEASE标志,该参数值必须为0。 •​ dwFreeType:释放类型,可以是MEM_DECOMMIT或MEM_RELEASE。 •​ 返回值:调用成功则返回一个非零值,否则返回0 •​ 6.VirtualLock 和VirtualUnLock •​ 函数功能:锁定和解锁虚拟内存页。一个进程可以分配一些页并将它锁定或者解锁,这样保证对他们使用不会出现缺页现象。解锁系统在必要时可以将这些页面换出到页面文件中。 •​ 函数原型:BOOL VirtualLock (LPVOID lpAddress, DWORD dwSize); •​ 函数参数: •​ lpAddress:加锁页面区域基址。 •​ dwSize:加锁区域的大小(以字节为单位)。 •​ 返回值:调用成功则返回一个非零值,否则返回0。 •​ 函数原型:BOOL VirtualUnlock (LPVOID lpAddress, DWORD dwSize); •​ 函数参数: •​ lpAddress:解锁页面区域基址。 •​ dwSize:解锁区域的大小(以字节为单位)。 •​ 返回值:调用成功则返回一个非零值,否则返回0。 实验分析 •​ 程序运行时,模拟线程进行各种虚存操作,这些活动导致了程序虚存空间和系统存储资源的变化。监控线程给出了监控到的这些活动和变化的信息。 •​ 程序给出了整个内存系统的各个全局统计量,包括物理内存使用量和页面文件使用情况等。通过这些统计量的变化可以分析当前的虚存活动对存储系统带来的变化。在分析的过程中可以帮助学生理解一些重要的内容,比如程序在进行一次虚存提交过后,显示的可用物理存储反而变多了,这是因为Windows的内存管理发现某个进程在一段时间没有运行后,会将它的部分页面转移到页面文件中去。所以虽然本实验程序的进程目前分配了一些内存,但是总的可用物理内存数量还是增多了。作为验证,可以检查一下此时可用页面文件的数量是否减少。 运行结果分析 •​ 1)保留一块区域的操作dwAvailVirtual(在用户状态下虚拟地址空间中的未保留与末 提交的存储空间)? •​ 2)提交一块区域的操作dwAvailPageFile(页面文件中可用的字节数) 和dwAvailPhys ? •​ 3)锁一块区域的操作dwAvailPhys(物理存储空间中可用的字节数)? •​ 4)解锁一块区域的操作dwAvailPhys? •​ 5)回收一块区域的操作dwAvailPageFile和dwAvailPhys?。 •​ 6)释放一块区域的操作dwavailvirtual? 基础知识: 在Windows系统中,任何一个进程都被赋予其自己的虚拟地址空间,该虚拟地址空间覆盖了一个相当大的范围,对于32位进程,其地址空间为232=4,294,967,296 Byte,这使得一个指针可以使用从0x00000000到0xFFFFFFFF的4GB范围之内的任何一个值。虽然每一个32位进程可使用4GB的地址空间,但并不意味着每一个进程实际拥有4GB的物理地址空间,该地址空间仅仅是一个虚拟地址空间,此虚拟地址空间只是内存地址的一个范围。进程实际可以得到的物理内存要远小于其虚拟地址空间。进程的虚拟地址空间是为每个进程所私有的,在进程内运行的线程对内存空间的访问都被限制在调用进程之内,而不能访问属于其他进程的内存空间。这样,在不同的进程中可以使用相同地址的指针来指向属于各自调用进程的内容而不会由此引起混乱。下面分别对虚拟内存的各具体技术进行介绍。 地址空间中区域的保留与释放   在进程创建之初并被赋予地址空间时,其虚拟地址空间尚未分配,处于空闲状态。这时地址空间内的内存是不能使用的,必须首先通过VirtualAlloc()函数来分配其内的各个区域,对其进行保留。VirtualAlloc()函数原型为: LPVOID VirtualAlloc(  LPVOID lpAddress,  DWORD dwSize,  DWORD flAllocationType,  DWORD flProtect );   其参数lpAddress包含一个内存地址,用于定义待分配区域的首地址。通常可将此参数设置为NULL,由系统通过搜索地址空间来决定满足条件的未保留地址空间。这时系统可从地址空间的任意位置处开始保留一个区域,而且还可以通过向参数flAllocationType设置MEM_TOP_DOWN标志来指明在尽可能高的地址上分配内存。如果不希望由系统自动完成对内存区域的分配而为lpAddress设定了内存地址(必须确保其始终位于进程的用户模式分区中,否则将会导致分配的失败),那么系统将在进行分配之前首先检查在该内存地址上是否存在足够大的未保留空间,如果存在一个足够大的空闲区域,那么系统将会保留此区域并返回此保留区域的虚拟地址,否则将导致分配的失败而返回NULL。这里需要特别指出的是,在指定lpAddress的内存地址时,必须确保是从一个分配粒度的边界处开始。   一般来说,在不同的CPU平台下分配粒度各不相同,但目前所有Windows环境下的CPU如x86、32位Alpha、64位Alpha以及IA-64等均是采用64KB的分配粒度。如果保留区域的起始地址没有遵循从64KB分配粒度的边界开始之一原则,系统将自动调整该地址到最接近的64K的倍数。例如,如果指定的lpAddress为0x00781022,那么此保留区域实际是从0x00780000开始分配的。参数dwSize指定了保留区域的大小。但是系统实际保留的区域大小必须是CPU页面大小的整数倍,如果指定的dwSize并非CPU页面的整数倍,系统将自动对其进行调整,使其达到与之最接近的页面大小整数倍。与分配粒度一样,对于不同的CPU平台其页面大小也是不一样的。在x86平台下,页面大小为4KB,在32位Alpah平台下,页面大小为8KB。在使用时可以通过GetSystemInfo()来决定当前主机的页面大小。参数flAllocationType和flProtect分别定义了分配类型和访问保护属性。由于VirtualAlloc()可用来保留一个区域也可以用来占用物理存储器,因此通过flAllocationType来指定当前要保留的是一个区域还是要占用物理存储器是意义的。其可能使用的内存分配类型有: 分配类型 类型说明 MEM_COMMIT 为特定的页面区域分配内存中或磁盘的页面文件中的物理存储 MEM_PHYSICAL 分配物理内存(仅用于地址窗口扩展内存) MEM_RESERVE 保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用 MEM_RESET 指明在内存中由参数lpAddress和dwSize指定的数据无效 MEM_TOP_DOWN 在尽可能高的地址上分配内存(Windows 98忽略此标志) MEM_WRITE_WATCH 必须与MEM_RESERVE一起指定,使系统跟踪那些被写入分配区域的页面(仅针对Windows 98)   分配成功完成后,即在进程的虚拟地址空间中保留了一个区域,可以对此区域中的内存进行保护权限许可范围内的访问。当不再需要访问此地址空间区域时,应释放此区域。由VirtualFree()负责完成。其函数原型为: BOOL VirtualFree(  LPVOID lpAddress,  DWORD dwSize,  DWORD dwFreeType );   其中,参数lpAddress为指向待释放页面区域的指针。如果参数dwFreeType指定了MEM_RELEASE,则lpAddress必须为页面区域被保留时由VirtualAlloc()所返回的基地址。参数dwSize指定了要释放的地址空间区域的大小,如果参数dwFreeType指定了MEM_RELEASE标志,则将dwSize设置为0,由系统计算在特定内存地址上的待释放区域的大小。参数dwFreeType为所执行的释放操作的类型,其可能的取值为MEM_RELEASE和MEM_DECOMMIT,其中MEM_RELEASE标志指明要释放指定的保留页面区域,MEM_DECOMMIT标志则对指定的占用页面区域进行占用的解除。如果VirtualFree()成功执行完成,将回收全部范围的已分配页面,此后如再对这些已释放页面区域内存的访问将引发内存访问异常。释放后的页面区域可供系统继续分配使用。   下面这段代码演示了由系统在进程的用户模式分区内保留一个64KB大小的区域,并将其释放的过程: // 在地址空间中保留一个区域 LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE, PAGE_READWRITE); …… // 释放已保留的区域 VirtualFree(bBuffer, 0, MEM_RELEASE); 物理存储器的提交与回收   在地址空间中保留一个区域后,并不能直接对其进行使用,必须在把物理存储器提交给该区域后,才可以访问区域中的内存地址。在提交过程中,物理存储器是按页面边界和页面大小的块来进行提交的。若要为一个已保留的地址空间区域提交物理存储器,需要再次调用VirtualAlloc()函数,所不同的是在执行物理存储器的提交过程中需要指定flAllocationType参数为MEM_COMMIT标志,使用的保护属性与保留区域时所用保护属性一致。在提交时,可以将物理存储器提交给整个保留区域,也可以进行部分提交,由VirtualAlloc()函数的lpAddress参数和dwSize参数指明要将物理存储器提交到何处以及要提交多少物理存储器。   与保留区域的释放类似,当不再需要访问保留区域中被提交的物理存储器时,提交的物理存储器应得到及时的释放。该回收过程与保留区域的释放一样也是通过VirtualFree()函数来完成的。在调用时为VirtualFree()的dwFreeType参数指定MEM_DECOMMIT标志,并在参数lpAddress和dwSize中传递用来标识要解除的第一个页面的内存地址和要释放的字节数。此回收过程同样也是以页面为单位来进行的,将回收设定范围所涉及到的所有页面。下面这段代码演示了对先前保留区域的提交过程,并在使用完毕后将其回收: // 在地址空间中保留一个区域 LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE, PAGE_READWRITE); // 提交物理存储器 VirtualAlloc(bBuffer, 65536, MEM_COMMIT, PAGE_READWRITE); …… // 回收提交的物理存储器 VirtualFree(bBuffer, 65536, MEM_DECOMMIT); // 释放已保留的区域 VirtualFree(bBuffer, 0, MEM_RELEASE);   由于未经提交的保留区域实际是无法使用的,因此在编程过程中允许通过一次VirtualAlloc()调用而完成对地址空间的区域保留及对保留区域的物理存储器的提交。相应的,回收、释放过程也可由一次VirtualFree()调用来实现。上述代码可按此方法改写为: // 在地址空间中保留一个区域并提交物理存储器 LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); …… // 释放已保留的区域并回收提交的物理存储器 VirtualFree(bBuffer, 0, MEM_RELEASE | MEM_DECOMMIT);   页文件的使用   在前面曾多次提到物理存储器,这里所说的物理存储器并不局限于计算机内存,还包括在磁盘空间上创建的页文件,其存储空间大小为计算机内存和页文件存储容量之和。由于通常情况下磁盘存储空间要远大于内存的存储空间,因此页文件的使用对于应用程序而言相当于透明的增加了其所能使用的内存容量。在使用时,由操作系统和CPU负责对页文件进行维护和协调。只有在应用程序需要时才临时将页文件中的数据加载到内存供应用程序访问之用,在使用完毕后再从内存交换回页文件。   进程中的线程在访问位于已提交物理存储器的保留区域的内存地址时,如果此地址指向的数据当前已存在于内存,CPU将直接将进程的虚拟地址映射为物理地址,并完成对数据的访问;如果此数据是存在于页文件中的,就要试图将此数据从页文件加载到内存。在进行此处理时,首先要检查内存中是否有可供使用的空闲页面,如果有就可以直接将数据加载到内存中的空闲页面,否则就要从内存中寻找一个暂不使用的可释放的页面并将数据加载到此页面。如果被释放页面中的数据仍为有效数据(即以后还会用到),就要先将此页面从内存写入到页文件。在数据加载到内存后,仍要在CPU将虚拟地址映射为物理地址后方可实现对数据的访问。与对物理存储器中数据的访问有所不同,在运行可执行程序时并不进行程序代码和数据的从磁盘文件到页文件的复制过程,而是在确定了程序的代码及其数据的大小后,由系统直接将可执行程序的映像用作程序的保留地址空间区域。这样的处理方式大大缩短了程序的启动时间,并可减小页文件的尺寸。 对内存的管理   使用虚拟内存技术将能够对内存进行管理。对当前内存状态的动态信息可通过GlobalMemoryStatus()函数来获取。GlobalMemoryStatus()的函数原型为: VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);   其参数lpBuffer为一个指向内存状态结构MEMORYSTATUS的指针,而且要预先对该结构对象的数据成员进行初始化。MEMORYSTATUS结构定义如下: typedef struct _MEMORYSTATUS {  DWORD dwLength; // MEMORYSTATUS结构大小  DWORD dwMemoryLoad; // 已使用内存所占的百分比  DWORD dwTotalPhys; // 物理存储器的总字节数  DWORD dwAvailPhys; // 空闲物理存储器的字节数  DWORD dwTotalPageFile; // 页文件包含的最大字节数  DWORD dwAvailPageFile; // 页文件可用字节数  DWORD dwTotalVirtual; // 用户模式分区大小  DWORD dwAvailVirtual; // 用户模式分区中空闲内存大小 } MEMORYSTATUS, *LPMEMORYSTATUS; 下面这段代码通过设置一个定时器而每隔5秒更新一次当前系统对内存的使用情况: // 设置定时器 SetTimer(0, 5000, NULL); …… void CSample22Dlg::OnTimer(UINT nIDEvent) {  // 获取当前内存使用状态  MEMORYSTATUS mst;  GlobalMemoryStatus(&mst);  // 已使用内存所占的百分比  m_dwMemoryLoad = mst.dwMemoryLoad;  // 物理存储器的总字节数  m_dwAvailPhys = mst.dwAvailPhys / 1024;  // 空闲物理存储器的字节数  m_dwAvailPageFile = mst.dwAvailPageFile / 1024;  // 页文件包含的最大字节数  m_dwAvailVirtual = mst.dwAvailVirtual / 1024;  // 页文件可用字节数  m_dwTotalPageFile = mst.dwTotalPageFile / 1024;  // 用户模式分区大小  m_dwTotalPhys = mst.dwTotalPhys / 1024;  // 用户模式分区中空闲内存大小  m_dwTotalVirtual = mst.dwTotalVirtual / 1024;  // 更新显示  UpdateData(FALSE);  CDialog::OnTimer(nIDEvent); }   对内存的管理除了对当前内存的使用状态信息进行获取外,还经常需要获取有关进程的虚拟地址空间的状态信息。可由VirtualQuery()函数来进行查询,其原型声明如下: DWORD VirtualQuery(  LPCVOID lpAddress, // 内存地址  PMEMORY_BASIC_INFORMATION lpBuffer, // 指向内存信息结构的指针  DWORD dwLength // 内存的大小 );   其中lpAddress参数为要查询的虚拟内存地址,该值将被调整到最近的页边界处。当前计算机的页面大小可通过GetSystemInfo()函数获取,该函数需要一个指向SYSTEM_INFO结构的指针作为参数,获取到的系统信息将填充在该数据结构对象中。下面这段代码通过对GetSystemInfo()的调用而获取了当前的系统信息: // 得到当前系统信息 GetSystemInfo(&m_sin); // 位屏蔽,指明哪个CPU是活动的 m_dwActiveProcessorMask = m_sin.dwActiveProcessorMask; // 保留的地址空间区域的分配粒度 m_dwAllocationGranularity = m_sin.dwAllocationGranularity; // 进程的可用地址空间的最小内存地址 m_dwMaxApplicationAddress = (DWORD)m_sin.lpMaximumApplicationAddress; // 进程的可用地址空间的最大内存地址 m_dwMinApplicationAddress = (DWORD)m_sin.lpMinimumApplicationAddress; // 计算机中CPU的数目 m_dwNumberOfProcessors = m_sin.dwNumberOfProcessors; // 页面大小 m_dwPageSize = m_sin.dwPageSize; // 处理器类型 m_dwProcessorType = m_sin.dwProcessorType; //进一步细分处理器级别 m_wProcessorLevel = m_sin.wProcessorLevel; // 系统处理器的结构 m_wProcessorArchitecture = m_sin.wProcessorArchitecture; // 更新显示 UpdateData(FALSE); VirtualQuery()的第二个参数lpBuffer为一个指向MEMORY_BASIC_INFORMATION结构的指针。VirtualQuery()如成功执行,该结构对象中将保存查询到的虚拟地址空间状态信息。MEMORY_BASIC_INFORMATION结构的定义为: typedef struct _MEMORY_BASIC_INFORMATION {  PVOID BaseAddress; // 保留区域的基地址  PVOID AllocationBase; // 分配的基地址  DWORD AllocationProtect; // 初次保留时所设置的保护属性  DWORD RegionSize; // 区域大小  DWORD State; // 状态(提交、保留或空闲)  DWORD Protect; // 当前访问保护属性  DWORD Type; // 页面类型 } MEMORY_BASIC_INFORMATION;   通过VirtualQuery()函数对由lpAddress和dwLength参数指定的虚拟地址空间区域的查询而获取得到的相关状态信息: // 更新显示 UpdateData(TRUE); // 虚拟地址空间状态结构 MEMORY_BASIC_INFORMATION mbi; // 查询指定虚拟地址空间的状态信息 VirtualQuery((LPCVOID)m_dwAddress, &mbi, 1024); // 保留区域的基地址 m_dwBaseAddress = (DWORD)mbi.BaseAddress; // 分配的基地址 m_dwAllocateBase = (DWORD)mbi.AllocationBase; // 初次保留时所设置的保护属性 m_dwAllocateProtect = mbi.AllocationProtect; // 区域大小 m_dwRegionSize = mbi.RegionSize; // 状态(提交、保留或空闲) m_dwState = mbi.State; // 当前访问保护属性 m_dwProtect = mbi.Protect; // 页面类型 m_dwType = mbi.Type; // 更新显示 UpdateData(FALSE);
/
本文档为【虚拟内存实验】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索