首页
社区
课程
招聘
[原创] Windows 7 x64 虚拟内存管理开篇
发表于: 2022-1-25 18:16 16082

[原创] Windows 7 x64 虚拟内存管理开篇

2022-1-25 18:16
16082

​ 这次打算写一系列的读书笔记。书名是《What makes it Page?》中译可为 Windows分页原理是什么?

 

Windows 7 x64 虚拟内存管理

​ 书的作者是Enrico Martignetti。书的封面是一个窗户,窗户的英文也是Window,并讲解了窗户的各个组成部分。这本书封面的灵感源自Joe Kaufman's 《What Makes It Go?, Work?, Fly?, Float?》

书中的源代码

源码链接

Windows内核版本

书上是6.1.7600.16385。我的镜像是7601.17514,遇到坑再说吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1: kd> lm v m nt
Browse full module list
start             end                 module name
fffff800`0445d000 fffff800`04a47000   nt         (pdb symbols)          C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\sym\ntkrnlmp.pdb\3844DBB920174967BE7AA4A2C20430FA2\ntkrnlmp.pdb
    Loaded symbol image file: ntkrnlmp.exe
    Image path: ntkrnlmp.exe
    Image name: ntkrnlmp.exe
    Browse all global symbols  functions  data
    Timestamp:        Sat Nov 20 17:30:02 2010 (4CE7951A)
    CheckSum:         0055CE0C
    ImageSize:        005EA000
    File version:     6.1.7601.17514
    Product version:  6.1.7601.17514
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        1.0 App
    File date:        00000000.00000000
    Translations:     0409.04b0
    Information from resource tables:
        CompanyName:      Microsoft Corporation
        ProductName:      Microsoft® Windows® Operating System
        InternalName:     ntkrnlmp.exe
        OriginalFilename: ntkrnlmp.exe
        ProductVersion:   6.1.7601.17514
        FileVersion:      6.1.7601.17514 (win7sp1_rtm.101119-1850)
        FileDescription:  NT Kernel & System
        LegalCopyright:   © Microsoft Corporation. All rights reserved.

目录

第一部分 Intel 架构基础

1 简介

2 权限级别

2.1 权限级别的定义

2.2 代码权限级别

2.3 环的定义

2.4 权限级别的转换

2.4.1 权限控制指令
2.4.2 硬件中断
2.4.3 处理器异常

3 栈

3.1 基本概念

3.2 0环栈 vs 3环栈

4 中断和异常

4.1 中断

4.1.1 中断的处理
4.1.2 中断返回
4.1.3 栈切换上的权限级别变化

4.2 异常

4.2.1 基本概念
4.2.2 处理器异常 vs Windows异常
4.2.3 异常的分类
4.2.3.1 异常
4.2.3.2 陷阱
4.2.3.3 中止

4.3 Interruptions

4.4 软中断

4.4.1 基本概念
4.4.2 处理器软中断 vs Windows 软中断

5 分页

5.1 分页概述

5.2 地址转换

5.2.1 地址转换概述
5.2.2 编码地址形式
5.2.3 虚拟地址到物理地址的转换
5.2.3.1 有效地址和无效地址
5.2.3.2 访问无效地址
5.2.3.3 有效地址的转换
5.2.3.4 物理地址变得无意义
5.2.3.5 多级地址空间
5.2.3.6 PxE映射范围
5.2.3.7 有效的PxE结构
5.2.3.8 大页的有效PDE
5.2.3.9 无效的PxEs
5.2.3.10 为什么有这些分页结构
5.2.3.11 TLB概述喝分页结构缓存

6 缓存控制

6.1 内存缓存是怎么被控制的

6.2 内存类型

6.2.1 回写型 (WB)
6.2.2 合并写型 (WC)
6.2.3 非缓存型(UC)

6.3 内存类型范围寄存器(MTRR)

6.4 页属性表 (PAT)

7 MSR寄存器

第二部分 Windows 内核基础

8 简介

9 用户模式和内核模式

10 IRQL

10.1 IRQL的定义

10.2 IRQL对代码执行的影响

10.2.1 简介
10.2.2 对处理器的影响
10.2.3 对线程的影响
10.2.3.1 IRQL大于等于2的中断
10.2.3.2 IRQL 为 1的中断
10.2.4 IRQL 为 1时的处理器线程切换
10.2.5 用户模式代码
10.2.6 编写中断处理函数的黄金法则
10.2.7 权限的提升和下降

11 软中断

11.1 基本概念

11.2 APC中断

11.3 DPC中断

12 对在DPC/dispatch或以上权限执行的代码的限制

13 历史痕迹:VAX/VMS中的IRQL和软中断

14 同步

14.1 同步问题

14.2 Passive 权限级别的同步

14.3 APC 权限级别的同步

14.4 High IRQL的同步

第三部分 内存管理基础

15 虚拟地址空间基础

15.1 基本范围

15.2 Paging Structres Region

15.2.1 PML4 自动入口
15.2.2 VA和PTE地址的关系
15.2.3 PDEs,PDPTEs,PML4Es的虚拟地址
15.2.4 Paging Structures Region Protection
15.2.5 !pte的用法说明

15.3 系统地址范围

15.3.1 系统地址范围从哪里开始?
15.3.2 主系统区域

16 NUMA系统简介

16.1 什么是NUMA系统

16.2 Windows对NUMA的支持

17 缓存和页面着色

17.1 缓存基础

17.2 页面着色

17.3 页面着色与其他缓存的对比

17.4 一级缓存

第四部分 用户范围内存管理

18 用户范围的内容

19 用户范围内存申请

20 虚拟内存管理数据结构

20.1 虚拟地址描述符 VAD

20.1.1 部分提交范围
20.1.2 回收子范围
20.1.3 VAD和内存保护
20.1.4 VADs对NUMA的支持

20.2 虚拟地址空间位图

20.2.1 用户模式地址空间位图 (UMAB)
20.2.2 用户模式页表位图 (UMPB)
20.2.3 验证虚拟地址空间位图的存在性
20.2.3.1 UMAB
20.2.3.2 UMPB

20.3 进程工作集数据结构

20.3.1 工作集大小
20.3.2 工作集链表(WSL)的虚拟地址范围
20.3.3 WSL Entries (WSLE)
20.3.3.1 使用中的WSLEs
 

​ 20.3.3.3.1.1 WSL Entries的前5项

20.3.3.2 释放 Entries
20.3.4 _MMWSL结构体的其他成员
20.3.5 !wsle 调试器扩展命令
20.3.6 从虚拟地址到WSLE

20.4 页帧号数据库

21 物理页面状态

21.1 Active

21.2 Modified

21.3 Standby

21.4 Transition

21.5 Free

21.6 Zeroed

21.7 Rom

21.8 Bad

21.9 Modified no-write

22 页面链表

23 申请一块新的物理页面

24 状态转换概述

25 虚拟内存管理中的同步

25.1 工作集 Pushlock 锁

25.2 _MMPFN 锁

26 首次访问私有地址

26.1 初始检查和同步

26.2 检查内存访问和建立PTE保护

26.2.1 当PTE被发现为0时

26.2.1.1 实验:观察双重页错误的行为

26.2.2 当PTE不为0时

26.2.3 当一个或者多个分页结构需要被申请时

26.3 更新页表项使用计数

26.4 申请物理页,并在正确的节点上着色

26.4.1 默认 NUMA 节点的着色计算
26.4.2 显式节点的着色计算
26.4.2.1 显式节点和 Nonzero PTEs
26.4.3 用于分配的物理页列表
26.4.3.1 单向_MMPFN链表
26.4.3.2 PFN 链起来的 _MMPFN 列表
26.4.3.3 两种列表类型的对比
26.4.3.4 页面查询顺序
 

​ 26.4.3.4.1 从Zeroed 列表中进行页面分配

 

​ 26.4.3.4.2 从Free 列表中进行页面分配

 

​ 26.4.3.4.3 从Standby 列表中的页面分配

 

​ 26.4.3.4.3.1 更新指向页面的PTE

 

​ 26.4.3.4.3.2 页面优先级

26.5 更新页面_MMPFN

26.6 建立PFN和PTE中的控制比特位

26.6.1 基本PTE设置

26.6.2 设置页面为可写状态

26.6.2.1 一个有趣的行为

26.6.3 缓存控制位

26.6.4 PTE中的最后注意事项

26.7 _MMPFN里的缓存控制位以及TLB刷新因素

26.8 更新工作集和PTE

26.9 保护页面

26.9.1 概念
26.9.2 实现
26.9.3 用户栈的特殊处理

26.10 结论和后续

27 TLB延迟失效的影响

27.1 为什么需要延迟失效以及会带来哪些问题

27.2 系统临时映射的延迟失效

27.3 最后的考虑

28 工作集修剪

28.1 工作集大小限制

28.2 工作集管理器

28.2.1 平衡集管理器线程
28.2.2 内存上下文激活
28.2.3 进程修剪标准
28.2.4 WSLEs Age

28.3 解决故障的同时进行修剪

28.3.1 虚拟内存管理器是怎么追踪Standby页面的重复使用的

28.4 修剪对页面共享计数和引用计数的影响

28.5 锁定内存中的页面

29 脏页移除和换出

29.1 从工作集到Modified List

29.1.1 Modified List是做什么的
29.1.2 Modified List 详细介绍
29.1.3 怎么观察脏页的移除
29.1.4 Modified 页面的 PTE以及_MMPFN
29.1.5 TLB无效化
29.1.6 页面移除期间的同步

29.2 将页面内容写入到分页文件中

29.2.1 Standby List 详情
29.2.2 Modified 页面写入线程 (MPW线程)
29.2.3 Write I/O 期间的页面状态
29.2.4 Standby 页面的_MMPFN和PTE的最终状态
29.2.5 对簇读取的支持
29.2.6 观察MPW线程的工作
29.2.6.1 从Modified 页面获得_EPROCESS
29.2.6.2 观察MPW线程附加进程
29.2.6.3 观察MPW线程写入Modified页面
29.2.6.4 观察MPW线程处理连续的页面块
29.2.7 将Active页面写入分页文件
29.2.7.1 基本概念
29.2.7.2 来自调试会话的证据
29.2.8 关于在写入过程中的并发页面访问的说明

30 Standby 页面的重复利用

31 软故障

31.1 standby状态页下的软故障

31.1.1 读取访问
31.1.2 写入访问

32.2 Modified状态页下的软故障

32 访问一个正在写的页面

32.1 访问Modified页面

32.1.1 读取访问
32.1.2 写入访问

32.2 对Active页面的访问 - Clean页面只读的一个原因

33 Active Clean 页面状态的变化以及PTE的Dirty位

33.1 从工作集里移除Clean 页面

33.2 向Clean Active页面写入

33.3 PET Dirty位的管理总结

34 硬故障

34.1 簇

34.1.1 基本概念
34.1.2 决定簇的边界

34.2 为读取操作申请物理页面

34.2.1 List 搜索次序,页面着色以及NUMA支持
34.2.2 建立MDL

34.3 多个分页文件的支持

34.3.1 分页文件的选择
34.3.2 分页文件的配置

34.4 在读取过程中的状态

34.4.1 Falting线程状态
34.4.2 PTEs 状态
34.4.3 _MMPFN 状态

34.5 读取完成时的状态

34.5.1 Falting虚拟页面的状态
34.5.2 Prefetched虚拟页面的状态

34.6 Excluded 虚拟页面的状态

35 页面置入冲突

35.1 页面置入冲突概念

35.2 冲突保护变化

35.2.1 faulting PTE上的冲突
35.2.2 prefeteched PTE上的冲突

35.3 冲突释放和回收

35.3.1 释放或者回收Faulting页面
35.3.2 释放或者回收Prefetched页面

35.4 冲突页面错误

35.4.1 初始处理和支持块
35.4.2 同步处理
35.4.2.1 相同Faulting PTE上的冲突
35.4.2.2 在簇上的其他PTEs冲突
35.4.3 异步处理
35.4.3.1 异步处理何时发生
 

​ 35.4.3.1.1 ActiveFaultCount大于0

 

​ 35.4.3.1.2 第一个故障线程的I/O优先级小于2

35.4.3.2 异步读取是怎么建立的
35.4.3.3 场景A: 主线程获胜
 

​ 35.4.3.3.1 主线程采取的动作

 

​ 35.4.3.3.2 从线程采取的动作

35.4.3.4 场景B: 从线程获胜
 

​ 35.4.3.4.1 获胜的从现场采取的动作

 

​ 35.4.3.4.2 主线程采取的动作

 

​ 35.4.3.4.3 失败的从线程采取的动作

35.4.3.5 Prefetched PTEs上的冲突

35.5 页面置入冲突实验

35.5.1 概述
35.5.2 测试驱动和他的客户端
35.5.3 MemColls
35.5.4 例子:挂起一个置入操作

36 分页结构的分页

36.1 基本概念

36.2 分页结构的首次初始化

36.3 分页结构从Active到Modified或者Standby的转换

36.4 通过转换或者分页文件恢复一个分页结构

36.5 分页结构的页面状态图

37 用户范围共享内存

37.1 基本概念

37.2 共享内存,节区对象和原型PTE相关的API

37.3 建立共享内存区域

37.3.1 CreateFileMapping的影响
37.3.2 MapViewOfFileEx的影响
37.3.2.1 在不同进程中的虚拟地址映射
37.3.2.2 映射创建的数据结构
37.3.2.3 NUMA支持

37.4 首次访问共享页面

37.5 从工作集中移除一个共享页面

37.5.1 为所有进程中的活动状态页面执行操作
37.5.2 对处于活动状态的最后一个进程页面附加操作

37.6 写入Section页面到分页文件

37.7 软故障

37.8 Section页面重复利用

37.9 写入Clean Section页面

37.10 硬故障

37.11 原型PTEs的分页

37.11.1 分页池概述
37.11.2 在分页池里转储节区数据结构
37.11.3 原型PTEs的分页逻辑
37.11.4 解决原型PTEs被换出时的错误

37.12 释放共享内存

37.13 共享内存的状态图

37.14 共享内存和工作集列表项

37.14.1 VMM 怎么从VA获取他的WSLE
37.14.2 共享内存的问题和其他东西
37.14.3 解决方案:通用哈希表
37.14.4 哈希表细节
37.14.4.1 非直接哈希表
37.14.4.2 直接哈希表
37.14.4.3 使用哪个哈希表

38 内存映射文件

38.1 基本概念

38.2 映射数据文件

38.2.1 映射数据文件的Section对象
38.2.1.1 Section对象数据结构
38.2.1.2 拥有多个子Section的Section对象
38.2.1.3 Section对象图
38.2.1.4 映射文件节区的原型PTEs
38.2.1.5 __MMPTE_SUBSECTION vs _MMPTE_PROTOTYPE
38.2.1.6 节区对象和虚拟地址空间
38.2.2 首次访问映射文件页面
38.2.2.1 Clean/Dirty 状态
38.2.2.2 OriginalPte的内容
38.2.2.3 可执行保护
38.2.3 从工作集中移除映射文件页面
38.2.4 将映射文件页面写到文件
38.2.5 映射文件页面的重复利用
38.2.6 对一个Clean映射文件页面进行写入
38.2.7 释放文件映射内存
38.2.8 映射文件和常规文件系统I/O
38.2.8.1 通过Section对象进行I/O
38.2.8.2 更多关于Section对象和文件缓存
38.2.8.3 内存Section共享

38.3 Section偏移

38.4 映射映像文件

38.4.1 使用映像文件映射
38.4.2 映像文件格式概述
38.4.3 映射映像文件的节区对象
38.4.4 映射映像文件的硬件PTEs

38.5 Sections与文件关联起来的影响

38.6 写时复制

38.7 映射文件实验

38.7.1 MemTest程序
38.7.2 使用数据断点断到VMM的注意事项
38.7.3 创建多子Section的内存Section
38.7.4 为文件读取创建Section对象

39 调试 Modified Page Writer (MPW) 和 Mapped Page Writer (MPAW)

39.1 介绍

39.2 阻塞MPW线程

39.2.1 概述
39.2.2 实验

39.3 阻塞MAPW线程

39.3.1 介绍
39.3.2 实验

39.4 让一切停止运作

40 大页

40.1 为什么有大页以及限制

40.2 私有大页

40.2.1 分配
40.2.2 PxEs 和 _MMPFN

40.3 内存Section大页

40.3.1 分配和Section对象
40.3.2 无偏移的视图映射
40.3.3 带偏移的视图映射
40.3.3.1 映射视图
40.3.3.2 引用视图

40.4 大页实验

41 用户栈

42 用户模式堆

42.1 基本的堆管理器功能

42.2 低碎片堆

42.3 安全功能和!heap

42.4 调试功能

42.5 页堆

第五部分 系统范围内存管理

43 系统范围内存管理概述

43.1 系统区域类型

43.2 动态分配区域的虚拟地址空间管理

43.3 页面错误处理

43.4 同步

43.5 无效系统地址 vs 无效用户地址

44 系统区域

44.1 8TB限制

44.2 未使用的系统空间

44.3 PTE空间

44.3.1 描述
44.3.2 虚拟地址空间管理
44.3.3 页错误处理
44.3.4 工作集的区域

44.4 超空间

44.4.1 UMAB 子区域
44.4.2 UMPB 子区域
44.4.3 WSL 区域
44.4.3.1 区域内容
44.4.3.2 页错误处理
44.4.4 哈希表区域

44.5 共享系统页面

44.6 系统工作集

44.6.1 系统缓存工作集
44.6.2 分页池工作集
44.6.3 系统PTE工作集
44.6.4 页面错误处理

44.7 初始加载器映射

44.7.1 区域内容
44.7.2 页面错误处理

44.8 系统PTE

44.8.1 区域内容
44.8.1.1 区域目的和名字
44.8.1.2 MDL 映射
44.8.1.3 其他种类的内容
44.8.2 虚拟地址空间管理
44.8.2.1 分配粒度问题
44.8.2.2 解决方案:更多的位图
44.8.2.3 位图缓冲区的最大大小和地址
44.8.2.4 缓冲区的实际大小和扩展
44.8.2.5 额外的系统PTEs
44.8.2.6 使用Windbg进行位图分析
 

​ 44.8.2.6.1 位图初始化

 

​ 44.8.2.6.2 位图扩展

 

​ 44.8.2.6.3 位索引与虚拟地址的关系(MiSystemPteInfo)

 

​ 44.8.2.6.4 位索引与虚拟地址的关系(MiKernelStackPteInfo)

44.8.3 页面错误处理
44.8.4 MmSystemPtesWs: 仅用于内核模式镜像的工作集
44.8.4.1 MDL 映射
44.8.4.2 内核栈
44.8.4.3 设备内存映射
44.8.4.4 _MM_SESSION_SPACE 实例
44.8.4.5 总结

44.9 分页池

44.9.1 区域内容
44.9.2 虚拟地址空间管理
44.9.3 页面错误处理
44.9.4 换出和重复利用
44.9.5 不属于任何工作集的页面

44.10 会话空间

44.10.1 会话空间的目的
44.10.2 区域内容
44.10.2.1 会话控制结构
 

​ 44.10.2.1.1 _MM_SESSION_SPACE

 

​ 44.10.2.1.2 会话工作集列表

 

​ 44.10.2.1.3 会话空间位图

44.10.2.2 会话空间镜像文件
44.10.2.3 会话分页池
44.10.2.4 映射视图
44.10.3 页面错误处理

44.11 动态内核虚拟地址空间

44.11.1 区域内容
44.11.1.1 系统缓存视图
44.11.1.2 分页和非分页特殊池
44.11.2 虚拟地址空间管理
44.11.3 页面错误处理

44.12 PFN 数据库

44.12.1 区域内容
44.12.2 PFN数据库和物理地址空间
44.12.2.1 物理地址空间Map
44.12.2.2 物理地址空间大小
44.12.2.3 PFN数据库大小
44.12.2.4 PFN数据库项对应的映射设备
44.12.2.5 大页的使用

44.13 Free和Zeroed Lists的着色

44.14 非分页池

44.14.1 区域内容
44.14.2 虚拟地址空间管理

44.15 HAL和加载器的映射

45 系统范围实验

45.1 注意事项

45.2 系统范围测试菜单

45.3 接触系统范围

45.4 接触用户空间的无效页面结构

46 内核态镜像文件加载

46.1 会话空间外的镜像加载

46.1.1 内存节区不会被使用
46.1.2 内核模式镜像的可分页节区
46.1.3 大页的使用

46.2 会话空间的镜像加载

46.2.1 内存节区开始被使用
46.2.2 我认为我们不在用户态了
46.2.2.1 寻找win32k.sys的内存Section
46.2.2.2 Section原型PTEs vs win32k.sys 镜像

47 内核栈

47.1 为什么内核栈是特殊的

47.2 线程内核栈

47.2.1 线程栈 vs 上下文切换
47.2.2 分配,分页,__MMPFNs
47.2.3 保护页面
47.2.4 栈跳转
47.2.5 切换器
47.2.5.1 内核栈的换出
47.2.5.2 内核栈的换入

47.3 DPC栈

48 晦涩的虚拟内存管理概念

48.1 进程元页面

48.2 常驻可用内存

48.2.1 基本概念
48.2.2 每个处理器的计数器缓存
48.2.3 测试:内存锁定的影响
48.2.4 测试:消耗比当前可获得内存更多的可常驻内存

49 与缓存管理器交互

49.1 缓存读取概述

49.2 缓存物理内存的使用

49.3 预取技术

49.4 Modified No-Write 列表

50 内存对象性能计数

50.1 可用字节数

50.2 缓存字节数

50.3 缓存错误/秒

50.4 提交限制

50.5 已提交字节数

50.5.1 基本概念
50.5.2 文件备份内存节区的影响
50.5.3 Demand Zero Faults
50.5.4 Standby 页面
50.5.5 系统范围地址

50.6 Demand Zero错误/秒

50.7 Free 和 Zero 页面链表字节数

50.8 Free 系统页表项

50.9 Modified 页面链表字节数

50.10 页面读取/秒

50.11 页面写入/秒

50.12 池非分页字节数

50.13 池分页常驻字节数

50.14 Standby 缓存计数

50.15 系统缓存常驻字节数

50.16 系统代码常驻字节数

50.17 系统驱动常驻字节数

附录

!pfn扩展命令

图表

简写

引用


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 5
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//