在应用程序中使用虚拟内存

——Windows核心编程学习手札之十五

Windows提供了3种进行内存管理的方法:

1)  虚拟内存,最适合用来管理大量对象或结构数组;

2)  内存映射文件,最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行的多个进程之间共享数据;

3)  内存堆栈,最适合用来管理大量的小对象;

用于管理虚拟内存的函数可以用来直接保留一个地址空间区域,将物理存储器(来自页文件)提交给该区域,并且可以设置你自己的保护属性。

通过调用VirtualAlloc函数,可以在进程的地址空间中保留一个区域:

PVOID VirtualAlloc(

PVOID pvAddress,

SIZE_T dwSize,

DWORD fdwAllocationType,

DWORD fdwProtect);

第一个参数pvAddress包含一个内存地址,用于设定想让系统将地址空间保留在什么地址,多数情况下,该参数传递NULL,用于告诉VirtualAlloc,保存一个空间地址区域的记录的系统应该将区域保留在它认为合适的任何地方。系统可以从进程的地址空间的任何位置来保留一个区域,因为不能保证系统可以从地址空间的底部向上或从上面向底部来分配各个区域,使用MEM_TOP_DOWN标志来说明该分配方式。

分配内存时,操作系统寻找一个大小满足需要的内存块,并返回内存块的地址,由于每个进程有自己的地址空间,可以设定一个基本内存地址,在这个地址上让操作系统保留地址空间区域。例如,将一个从50MB开始的区域保留在进程的地址空间中,传递pvAddress为52428800(50*1024*1024),如果该内存地址有一个足够大的空闲区域满足你的要求,那系统就保留这个区域并返回地址,如果在特定的地址上不存在空闲区域,或者空闲区域不够大,那系统就不满足需求,VirtualAlloc函数返回NULL。注意,为pvAddress参数传递的任何地址必须始终位于进程的用户方式分区中,否则对VirtualAlloc函数的调用就会失败,导致其返回NULL。地址空间区域总是按照分配粒度的边界来保留(迄今为止所有的Windows环境下都是64KB),因此,如果试图在进程地址空间中保留一个从19668992(300*65356+8192)这个地址开始的区域,系统就会将这个地址保留为64KB的倍数,即19660800(300*65356)开始的区域。如果VirtualAlloc函数满足了需求,就返回保留区域的基地址,如果传递一个指定的地址作为VirtualAlloc的pvAddress参数,那么该返回值与传递给VirtualAlloc的值相同,并取为64KB的整数倍。

VirtualAlloc函数的第二个参数是dwSize,用于设定想保留的区域大小(以字节为单位),系统保留的区域必须是CPU页面大小的倍数,如试图保留一个跨越62KB的区域,结果就会在使用4KB/8KB或16KB页面的计算机上产生一个跨越64KB的区域。

VirtualAlloc函数的第三个参数是fdwAllocationType,告诉系统想保留一个区域还是提交物理存储器(VirtualAlloc函数也可以用来提交物理存储器),若要保留一个地址空间区域,传递MEM_RESERVE标识符作为fdwAllocationType参数的值。如果保留区域预计在很长时间内不会被释放,那可以在尽可能高的内存地址上保留该区域,这样,该区域就不会从进程地址空间的中间位置上进行保留,因此这个位置可能导致区域分成碎片,如果想让系统在最高内存地址上保留一个区域,需为pvAddress参数和fdwAllocationType参数传递NULL,还必须逐位使用OR将MEM_TOP_DOWN标志和MEM_RESERVE标志连接起来。

VirtualAlloca最后一个参数是fdwProtect,用于指明应该赋予该地址空间区域的保护属性。与该区域相关联的保护属性对映射到该区域的已提交内存没有影响,无论赋予区域的保护属性是社呢,如果没有提交任何物理存储器,那访问该范围中的内存地址的任何企图都将导致该线程引发一个访问违规。

当保留一个区域后,必须将物理存储器提交给该区域,然后才能访问该区域中包含的内存地址,系统从它的页文件中将已提交的物理存储器分配给一个区域,物理存储器总是按页面边界和页面大小的块来提交的。若要提交物理存储器,须再次调用VirtualAlloc函数,设置参数fdwAllocationType为MEM_COMMIT,传递的页面保护属性一般与调用VirtualAlloc来保留区域时使用的保护属性相同(大多数情况下是PAGE_READWRITE)。在已保留的区域中,须告诉VirtualAlloc函数,要将物理存储器提交到那里以及提交多大物理存储空间,实现这一点,需要在pvAddress参数中设定需要的内存地址,并在dwSize参数上设定物理存储器的大小。

提交物理存储器的例子:应用程序在X86CPU上运行,保留了一个从地址5242880开始的512KB区域,现在将物理存储器提交给已保留区域的6KB部分,从2KB的地方开始,直到已保留区域的地址空间。可调用带有MEM_COMMIT标志的VirtualAlloc函数:

VirtualAlloc((PVOID)(5242880+(2*1024)),6*1024,MEM_COMMIT,PAGE_READWRITE);

例子中,系统必须提交8KB的物理存储器,地址范围从5242880到5251071(5242880+8KB-1),这两个提交的页面都拥有PAGE_READWRITE保护属性,保护属性在整个页面单位内生效,同一个内存页面的不同部分不能使用不同的保护属性,但在不同区域中的一个页面可以使用两种以上保护属性。

若要回收映射到一个区域的物理存储器,或者释放这个地址空间区域,可调用VirtualFree函数:

BOOL VirtualFree(

LPVOID pvAddress,

SIZE_T dwSize,

DWORD fdwFreeType);

当进程不再访问区域中的物理存储器,可以释放整个保留的区域和所有提交给该区域的物理存储器,方法是一次调用VirtualFree函数。pvAddress是释放区域的基地址,与该区域被保留时VirtualAlloc函数返回的地址相同,系统知道在特定内存地址上的区域大小,因此dwSize参数可以为零,实际,该参数必须传递零,否则调用VirtualFree失败,对最后一个参数fdwFreeType,必须传递MEM_RELEASE,以告诉系统将所有映射的物理存储器提给该区域并释放该区域,当释放一个区域时,必须释放该区域保留的所有地址空间。如不想保留128KB的区域,不能只释放64KB。当想要从一个区域回收某些物理存储器,但是却不释放该区域,设置参数fdwFreeType为MEM_DECOMMIT标志。回收时也按照页面的分配粒度来进行,设定一个页面中间的一个内存地址就可以回收整个页面,如果pvAddress+dwSize的值位于一个页面的中间,那包含该地址的整个页面将被回收,因此,位于pvAddress至pvAddress+dwSize范围内的所有页面均被回收。

在应用程序中使用虚拟内存——Windows核心编程学习手札之十五相关推荐

  1. 虚拟内存——Windows核心编程学习手札之十四

    虚拟内存 --Windows核心编程学习手札之十四 系统信息 有些操作系统的值是根据主机而定的,如页面大小.分配粒度大小等,这些值不用硬编码形式,进程初始化时应检索这些值以使用.函数GetSystem ...

  2. 对程序错误的处理——Windows核心编程学习手札之一

    对程序错误的处理 --Windows核心编程学习手札之一 函数被调用执行时,先检验传递给它的各个参数的有效性,后执行任务.函数执行中若因参数无效或因某种原因导致无法正常完成函数任务,那么操作系统会返回 ...

  3. DLL基础——Windows核心编程学习手札之十九

    DLL基础 --Windows核心编程学习手札之十九 Windows API中的所有函数都包含在DLL中,3个最重要的DLL是Kernel32.dll,它包含用于管理内存.进程和线程的各个函数:Use ...

  4. 堆栈——Windows核心编程学习手札之十八

    堆栈 --Windows核心编程学习手札之十八 堆栈可以用来分配许多较小的数据块,可以不考虑分配粒度和页面边界之类的问题,但分配和释放内存块的速度比其他机制要慢,并且无法直接控制物理存储器的提交和回收 ...

  5. 线程的堆栈——Windows核心编程学习手札之十六

    线程的堆栈 --Windows核心编程学习手札之十六 系统会在进程的地址空间内保存一些区域,同时也会在进程地址空间内为线程的堆栈保留区域.线程都有自己的堆栈,创建时,系统就保留一个堆栈空间区域,并将相 ...

  6. 用户方式中线程的同步——Windows核心编程学习手札之八

    用户方式中线程的同步 --Windows核心编程学习手札之八 系统中所有线程都必须拥有对各种系统资源的访问权,这些资源包括内存堆栈.串口.文件.窗口和许多其他资源.如果一个线程需要独占对资源的访问权, ...

  7. 未处理异常和C++异常——Windows核心编程学习手札之二十五

    未处理异常和C++异常 --Windows核心编程学习手札之二十五 当一个异常过滤器返回EXCEPTION_CONTINUE_SEARCH标识符时是告诉系统继续上溯调用树,寻找另外的异常过滤器,但当每 ...

  8. 插入DLL和挂接API——Windows核心编程学习手札之二十二

    插入DLL和挂接API --Windows核心编程学习手札之二十二 如下情况,可能要打破进程的界限,访问另一个进程的地址空间: 1)为另一个进程创建的窗口建立子类时: 2)需要调试帮助时,如需要确定另 ...

  9. DLL的高级操作技术——Windows核心编程学习手札之二十

    DLL的高级操作技术 --Windows核心编程学习手札之二十 显示加载DLL模块: HINSTANCE LoadLibrary(PCTSTR pszDLLPathName); HINSTANCE L ...

最新文章

  1. Linux学习第三章
  2. SQL一对多特殊查询,取唯一一条
  3. java ee cdi_Java EE CDI处理程序方法示例
  4. 比python好_这就是为什么Python比R更好的原因
  5. 判断一个点是否在矩形内部_矩形、圆形泄水管规格型号优势
  6. web.config 测试账号
  7. AAtitit.随时间变色特效 ---包厢管理系统的规划titit.随
  8. 异步ajax动态实现级联,JQuery异步加载无限下拉框级联功能实现示例
  9. 啊哈算法-bfs-解救小哈
  10. 【bzoj2242】计算器 离散对数
  11. html速成项目,HTML速成教材.doc
  12. CCCC选拔赛赛后总结
  13. ES 搜索19 (match 查询时权重提升)
  14. 【Python教程】十四、找不到对象?用Python自己写一个|类与对象(二)
  15. Revit二次开发环境平台的搭建
  16. 半加器与全加器的实现
  17. 怎么实现在MindMapper中添加便笺
  18. 【三维目标检测】Complex-Yolov4详解(一): 数据处理
  19. CSS的content属性怎么用?
  20. 刷脸支付不需要掏手机扫码很是方便

热门文章

  1. 项目管理project甘特图模板_【八大项目管理应用】必用项目管理工具对比推荐...
  2. Linux上iptables防火墙的基本应用教程
  3. 技术分享:几种常见的JavaScript混淆和反混淆工具分析实战【转】
  4. SpringBoot+Mybatis 框架之 @Select注解方式搭建
  5. Content Security Policy的学习理解
  6. 手脱FSG 2.0 - bart/xt
  7. IDE-Android Studio -FAQ-使用习惯(不断更新 欢迎留言)
  8. 表单美化-原生javascript和jQuery下拉列表(兼容IE6)
  9. Android中网络编程出错
  10. new和override