接着上一篇 Windows核心编程 - 在应用程序中使用虚拟内存(1)
4. 何时调拔物理存储器
假设正在实现一个电子表格的应用程序,需要一个CELLDATA结构来描述单元格的内容。一种最简单的方式使用二维数组这种数据结构来实现,数组的优点是访问速度快,但缺点是意味着程序一开始运行就需要从页交换文件中分配大量的物理存储器,但实际上用户通常只会使用少数的单元格,造成内存的浪费。 另一个方式是可使用链表这种数据结构实现动态增加,但缺点是读取速度慢,因为需要遍历。虚拟内存技术提供了一种折中的方案,既能利用数组结构的快速访问, 又能更好地节点物理存储器,执行步骤如下:
1)预订一块足够大的区域来容纳CELLDATA结构的整个数组,只是预订区域,所以不会消耗物理存储器;
2)当用户在某个单元格输入数据时,首先确定CELLDATA结构在区域中的内存地址。 由于这时该内存址还没映射物理存储器,因此还不能访问,否则会引发违规;
3)给第2)步中的内存地址调拔足够的物理存储器。
4)设置CELLDATA结构的成员。这时内存地址已经映射到物理存储器,于是程序就可以访问存储器了。
由于系统是按页面的粒度来调拔物理存储器的,因此当试图给一个CELLDATA结构调拔物理存储器时,实现上是会给整个页面调拔存储器,这有个好处是: 当用户在相邻的单元格输入数据时,就不需要再调拔物理存储器了,因为是在同一页面,已经调拔过了。
有几种方法可以用来确定是否需要给区域中的某一部分调拔物理存储器,其中最佳的方式是使用结构化异常处理(SEH):
给应用程序设置一个异常处理程序, 当程序试图访问尚未调拔物理存储器的内存地址时,引发异常,系统会通知应用程序,接着应用程序 就可调拔物理存储器,并告诉系统再重新执行那条引发异常的指令。这时内存访问会成功,程序 继续运行。
5.撤销调拔物理存储器及释放区域
撤销调拔给区域的物理存储器,或是释放地址空间中的一整块区域,可用调用函数:
BOOL VirtualFree(
LPVOID pvAddress,
SIZE_T dwSize,
DWORD fdwFreeType)
如果是撤销调拔给该区域的所有物理存储器,并释放整个区域,需指定MEM_RELEASE的fdwFreeType,释放时须释放该区域预订的所有地址空间;
如果想撤销调拔给区域的一部分物理存储器,不想释放整个区域,则传递页面的地址给pvAddress,指定MEM_DECOMMIT的fdwFreeType。由于撤销调拔物理存储器也是基于页面粒度的,所以当给定的内存地址是位于一个页面的中间,则系统会撤销调拔整个页面。一旦系统撤销了调拔给页面的物理存储器,所释放的物理存储器就可以用于系统中的其它进程,这时试图访问该页面的内存地址将会引发访问违规。