Windows内存管理学习笔记(三)—— 无处不在的缺页异常
缺页异常
描述:
- 例:当CPU访问一个地址,其PTE的P位(页面有效位)为0,此时会产生缺页异常
- 在windows中,缺页异常是时刻在发生的
PTE结构(10-10-12分页):
假设:当我们的物理内存大小只有2MB时,当我们需要用到某个物理页的时候,将对应物理页的P位置1,当我们不再需要使用该物理页时,再将其P位置0,这样的内存使用效率是非常低的,因此windows不是这样设计的——只有正在使用的线性地址,才会被挂上物理页,当物理页在一段时间内不被使用或不够用的情况下,操作系统会将当前物理页中的数据拷贝至硬盘中,然后将当前物理页供给“别人”使用,当需要再次用到该物理页的数据时,则将“别人”的数据再拷贝出来,将“自己"的数据从硬盘中拷回,被拷至硬盘中的物理页数据会被写入到一个文件中,也就是我们平时常说的虚拟内存
实验一:设置虚拟内存
1)右键”我的电脑“-属性-高级-”性能“设置-高级
2)将虚拟内存大小设置为780MB
3)查看c:\pagefile.sys文件属性(需设置文件夹选项-显示隐藏文件)
当物理页占用情况较为紧张时,这个文件用于存放物理页的临时数据作为备份
无处不在的缺页
描述:当线性地址对应的物理页被存储到文件中时,PTE结构被拓展为以下四种情形
位于页面文件
描述:
- 当物理页被放入页面文件时,PTE结构如图a所示
- 当该进程再次读取该物理页对应的线性地址时,由于P位为0,因此会触发缺页异常处理程序
- 在windows xp中,缺页异常处理程序位于IDT[0xE]
- 此时,缺页异常处理程序会再次查询当前PTE的属性,若1-4位,5-9位,12-31位都有值,说明当前PTE是有效的,但是物理页被存放到了页面文件中
- 然后,缺页异常处理程序根据PTE中得到的值去查询pagefile.sys,将原来的物理页的内容挂入新的物理页,然后将P位置1,再将新的物理页的物理地址挂入PTE的第12-31位
- 在用户看来,两次读取同一个线性地址,中间这一系列复杂过程是透明的,而只能观察到两次都成功读取了同一个值
保留与提交的误区
我们对于VirtualAlloc的传统概念
LPVOID VirtualAlloc{
LPVOID lpAddress,
DWORD dwSize,
DWORD flAllocationType, // 分配的类型
// MEM_RESERVE:只保留线性地址,不分配物理页
// MEM_COMMIT:既保留线性地址,又分配物理页
DWORD flProtect
};
思考:如果设置flAllocationType成员的值为MEM_COMMIT,当函数执行结束之后,马上就会为当前线性地址分配物理页吗?
答案:不会,只有当线性地址真正被使用时,才会分配物理页
实验二:理解缺页异常
1)编译并运行以下代码
#include <stdio.h>
#include <windows.h>
int main()
{
LPVOID pAddr = VirtualAlloc(NULL, 0x1000*8, MEM_COMMIT, PAGE_READWRITE);
printf("%p\n", pAddr);
getchar();
*(PDWORD)pAddr = 0x12345678;
getchar();
return 0;
}
2)运行结果
3)查看pAddr所在的线性地址是否存在物理页
kd> !process 0 0
可以看到,此时并未被挂上具体物理页(PTE为空)
备注:上述步骤也可以使用以下命令进行代替
4)继续运行程序,再次查看PTE
此时线性地址被挂上物理页(PTE有值)
5)观察VadRoot
标红行的8指的是最多可以为这块线性地址分配8个物理页(0-7),但不是每块线性地址都已被挂上有效的物理页,只有当对应线性地址块被使用时,才会真正被挂上物理页
6)观察_MMVAD结构体
当访问一个未被挂上物理页的线性地址时,会触发缺页异常,缺页异常处理程序会去查找VadRoot,若当前线性地址已被分配,则会将其挂上物理页,否则会触发内存访问错误
总结:缺页异常使得物理页的使用更加高效
EXECUTE_WRITECOPY
描述:当程序正常加载一个EXE或者DLL时,其VadRoot中的内存属性为Mapped EXE,权限为EXECUTE_WRITECOPY,当试图对其某一内容进行修改时,无法对其它进程中的同一个模块产生影响
原理:
- 当试图对其某一内容进行修改时,系统会检查其线性地址对应的物理页的PTE属性,若R/W属性为0时,触发缺页异常
- 缺页异常处理程序检查VadRoot中对应线性地址所在空间的MMVAD_FLAGS中的Protection成员,若值位0x111时,表示权限为EXECUTE_WRITECOPY
- 此时,缺页异常处理程序将当前线性地址对应的物理页内容进行拷贝,将其写入一个新的物理页,然后将当前进程需要修改的线性地址挂入新的物理页上,然后对需要修改的内容进行写入
解决方法:可通过编写驱动程序先将需要修改的线性地址对应的物理页的PTE的R/W位置为1,再对需要修改的地方进行修改