《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存

上传人:我*** 文档编号:135995003 上传时间:2020-06-22 格式:DOC 页数:17 大小:30.50KB
返回 下载 相关 举报
《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存_第1页
第1页 / 共17页
《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存_第2页
第2页 / 共17页
《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存_第3页
第3页 / 共17页
《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存_第4页
第4页 / 共17页
《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存_第5页
第5页 / 共17页
点击查看更多>>
资源描述

《《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存》由会员分享,可在线阅读,更多相关《《Windows核心编程系列》十三谈谈在应用程序中使用虚拟内存(17页珍藏版)》请在金锄头文库上搜索。

1、Windows核心编程系列十三谈谈在应用程序中使用虚拟内存 在应用程序中使用虚拟内存 Windows提供了以下三种机制对内存进行操控: 一:虚拟内存。最适合来管理大型对象数据或大型结构数组。 二:内存映射文件。最适合用来管理大型数据流,以及在同一机 器上运行的多个进程之间共享数据。 三:堆。最适合用来管理大量的小型对象。 很多人都对VirtualAlloc和malloc 或new的区别不是很清楚,我也一样。今天搜索下了,发现这句话说的很清楚了: VirtualAlloc要进入内核模式,算法特复杂,比较慢,而且分配粒度是4k,用来分配小块内存很浪费 malloc先用VirtualAlloc弄一大

2、块内存,后面在堆上分配时就不用进入内核模式,算法也简单些,而且分配粒度比较小 VirtualAlloc只能分配4KB为单位的页面,适合大型数据或者内存映射文件等用途。而堆的申请分配就没有这个限制,更为灵活。 有的人嫌malloc还不够精简,于是又在堆上面开辟自己的内存池,更加轻量级 本文将主要介绍虚拟内存。 Windows提供了一些用来操纵虚拟内存 的函数,我们可以通过这些函数直接预订地址空间区域,并给这些预订的区域调拨来自页交换文件的物理存储器。 预定地址空间区域。 可以通过调用VirtualAlloc函数来运行: cpp view plaincopyPVOID VirtualAlloc(

3、PVOID pvAddress, SIZE_T dwSize, DWORD fdwAllocationType, DWORD fdwProtect); pvAddress是内存地址。用来告诉我们想要运行地址空间中的哪一块。 当传入NULL时,系统会自动找到一块闲置区域。 如果在pvAddress标识的内存块中找不到闲置区域,或闲置区域不够大函数将返回NULL。 如果VirtualAlloc能满足我们的要求,它会预定一块区域并返回该区域的基地址。 dwSize用来指定我们想要预订的区域大小。以字节为单位。 系统始终以cpu页面大小整数倍来预定区域。且起始地址是按照分配粒度64kB的整数倍来预定的

4、。 fdwAllocationType用来告诉系统我们到底是要预订还是要调拨物理存储器。如果要预订区域可以传入:MEM_RESERVE。如果我们想让系统从尽可能高的内存地址来预定区域,必须传入NULL给pvAddress,同时对MEM_TOP_DOWN和MEM_RESERVE标志进行按位或操作。 fdwProtect给区域指定保护属性。区域的保护属性对调拨给该区域的物理存储器不起任何作用。无论指定何种保护属性,只要还未给该区域调拨物理存储器都会导致访问违规。 预订时指定的属性应该跟调拨时指定的属性相同,这样系统内部处理效率会更高。 调拨物理存储器 预定区域后还需要给该区域调拨物理存储器。系统会

5、从页交换文件中调拨物理存储器给该区域。在调拨物理存储器时,起始地址和区域大小始终都是页面大小的整数倍。调拨物理存储器同样需要调用VirtualAlloc。但这次我们需要传入MEM_COMMIT来作为fdwAllocationType的值。 pvAddress标识要调拨物理存储器的区域的起始地址。 dwSize表示物理存储器的大小。以字节为单位。我们并不需要给整个区域都调拨物理存储器。 有了起始地址和大小就可以标识一段区域。 同时预定和调拨物理存储器 有时我们项同时预定区域并给该区域调拨物理存储器。同样需要调用VirtualAlloc。但是MEM_RESERVE要和MEM_COMMIT按位或并将

6、它们传给fdwAllocationType。此时系统会为整个区域调拨物理存储器。 撤销调拨物理存储器及释放区域。 要撤销调拨给区域的物理存储器或是释放地址空间中的一整块区域。可以调用VirtualFree函数: cpp view plaincopyBOOL VirtualFree( LPVOID pvAddress, SIZE_T dwSize, DWORD fdwFreeType); pvAddress参数必须是区域的基地址。该地址就是预定区域时VirtualAlloc返回的地址。由于系统在内部会记录该地址处的区域大小,因此我们可以且必须传入0给dwSize。 当传入MEM_RELEASE给

7、fdwFreeType时是想告诉系统撤销调拨给该区域的所有物理存储器,并释放该区域。 我们可以撤销调拨给该区域的一部分物理存储器。此时需要传入pvAddress来告诉系统我们想要撤销调拨的区域的起始地址。dwSize传入想要撤销调拨区域的物理存储器大小,并传入MEM_DECOMMIT给fdwFreeType。 和调拨物理存储器一样,撤销调拨也是基于页面粒度的。也就是说,如果给定的地址位于一个页面中,那么系统会撤销调拨整个页面。如果dwSize为0,而pvAddress又是该区域的基地址,那么VirtualFree会调拨给该区域的所有页面。 改变保护属性 实际中我们很少需要改变已调拨物理存储器的

8、保护属性。比如,我们可以写一个管理链表的程序,把链表中的节点保存在一个已预订的区域中。我们可以设计这样的链表处理函数,让它们在每个函数的开头把物理存储器的保护属性改成PAGE_READWRITE,访问后,再把保护属性改回FPAGE_NOACCESS。这样就可以保护链表数据免受程序中其他缺陷的影响。 我们可以调用VirtualProtect函数来改变一个内存页面的保护属性: cpp view plaincopyBOOL VirtualProtect( PVOID pvAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD pflOldProtect)

9、; pvAddress指向内存基地址。 dwSize表示要改变保护区域的大小,以字节为单位。 flNewProtect可以是除了PAGE_WRITECOPY和PAGE_EXECUTE_WRITECOPY之外的任何PAGE_*属性。pflOldProtect是一个指针,返回原来的属性。一定不可以传入NULL,必须传入一个有效的地址给VirtualProtect,否则函数执行失败。 由于保护属性是与整个物理存储页相关联的,VirtualProtect会改变pvAddress和dwSize跨越的所有页面的属性。而不会仅仅对一个字节改变属性,这是没有意义的。 重置物理存储器的内容 当我们修改物理内存页

10、时,系统会尽量把改动把持在内存中。当应用程序在运行时,可能需要将数据载入内存,系统会在内存中查找可用的页面。如果找到闲置页面,就将数据载入此页面。如果没有找到,系统就会采用一定的算法,置换一些页面。如果该页面已经被修改过,系统会将这些页面置换到页交换文件。 Windows提供一项特性,使得应用程序能够提高自身的性能。这一特性就是重置物理存储器。重置物理存储器的意思是告诉系统一个或几个物理存储器页中数据没有被修改过。在需要闲置页面时,可以直接将它们覆盖,而不需要将它们写入页交换文件。有些应用程序需要在一小段时间内使用存储器,之后就不需要保存存储器中的内容。为了提高性能,应用程序应该告诉系统此页面

11、没有被修改过,不要在页交换文件中保存存储页。为了重置存储器,应用程序应该调用VirtualAlloc函数并在第二个参数中传MEM_RESET标志。 cpp view plaincopyPINT pnData=(PINT)VirtualAlloc(NULL,1024, MEM_RELEASE|MEM_COMMIT,PAGE_READWRITE); pnData0=100; pnData1=200; VirtualAlloc(PVOID)pnData,sizeof(int),MEM_RESET,PAGE_READWRITE); 这段代码首先调拨了一块存储器,然后将这块内存的前4个字节标记为可以被重

12、置。但是重置存储器的操作会调用失败。第二个VirtualAlloc会返回NULL。GetLastError会返回ERROR_INVALID_ADDRESS。 这是因为在传入MEM_RESET给VirtualAlloc时,函数会把基地址向下取整到页面大小的整数倍。(其他标志时都是向上去整,此处要注意。)其目的是确保在同一页面中还有其他重要数据的情况下,不会把它们丢弃。前面的例子,向下去整到页面整数倍是0,这是没有意义的。也就是说我们重置的页面必须是一个或几个完整的页面。不足一页的为了保护数据就不将它们设为可重置。 还要注意MEM_RESET不能和其他标志一起使用,它不合群,只能单独使用。否则调用

13、会失败。 VirtualQuery函数。 VirtualQuery可以用来查询与地址空间有关的特定信息。比如大小,存储器类型及保护属性。 cpp view plaincopyDWORD VirtualQuery( LPCVOID pvAddress, PMEMORY_BASIC_INFORMATION pmbi, DWORD dwLength); pvAddress指定需要查询的虚拟内存地址。 dwLength指定MEMORY_BASIC_INFORMATION结构大小。 pmbi返回MEM_BASIC_INFORMATION结构地址。该地址定义如下:cpp view plaincopytyp

14、edef struct _MEMORY_BASIC_INFORMATION PVOID BaseAddress;/等于pvAddress向下取整到页面大小。 PVOID AllocationBase;/区域基地址。 DWORD AllocationProtect;/预定区域时的保护属性。 SIZE_T RegionSize;/区域大小。以字节为单位。以BaseAddress为起始地址。 DWORD State;/区域页面的状态。 DWORD Protect;/保护属性 DWORD Type;/ MEMORY_BASIC_INFORMATION,*PMEMORY_BASIC_INFROMATION; VirtualQueryEx与VirtualQuery的区别就是它可以传入一个进程句柄。也就意味着它可以查询其他进程地址空间的信息。 GlobalMemoryStatus可以用来取得当前内存状态的动态信息: cpp view plaincopyVOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer); MEMORYSTATUS结构定义如下

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 事务文书

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号