ntoskrnl\mm 源码学习与内存管理计划

📅 2026/7/19 23:38:52 👁️ 阅读次数 📝 编程学习
ntoskrnl\mm 源码学习与内存管理计划

ntoskrnl\mm 源码学习与内存管理计划

概述

本计划针对 ReactOS 内核内存管理器(ntoskrnl\mm\)的源码学习。MM 子系统由旧版 MM(根目录 10 个文件)和ARM3ARM3/子目录 30 个文件)两套实现组成,当前主力为 ARM3。整个目录包含约 49 个源文件 + 多个架构特定实现。


第一阶段:前置知识准备

1.1 硬件分页机制(预计 1-2 天)

在学习任何 MM 源码之前,必须先理解 CPU 的分页硬件机制。

主题关键概念参考材料
x86 32位分页页目录 (PD)、页表 (PT)、PTE 结构、4KB/4MB 页面Intel/AMD 手册 Vol 3
PAE 分页页目录指针表 (PDPT)、36位物理地址、64位 PTEIntel/AMD 手册 Vol 3
x86-64 分页PML4 → PDPT → PD → PT 四级页表AMD 手册 Vol 2
ARM 分页二级页表、段/大页/小页ARM 架构手册
TLB 刷新INVEPT、INVVPID、CR3 切换Intel/AMD 手册

练习:手绘 x86 分页地址转换图(虚拟地址 → PDE → PTE → 物理页)。

1.2 Windows 内存管理概念(预计 2-3 天)

概念说明MM 中对应
虚拟地址描述符 (VAD)描述进程地址空间中每个区域的 AVL 树节点vadnode.c,miarm.h
页帧号数据库 (PFN DB)每个物理页一个条目,记录引用计数、状态、映射pfnlist.c,freelist.c
工作集 (Working Set)进程当前在物理内存中的页面集合wslist.cpp,balance.c
段对象 (Section)文件与虚拟内存之间的映射抽象section.c(ARM3),section.c(旧版)
池 (Pool)内核堆分配器(分页/非分页)expool.c,pool.c
系统 PTE内核虚拟地址空间中的空闲页表项syspte.c
超空间 (Hyperspace)临时映射物理页到内核空间的机制hypermap.c
MDL描述物理内存缓冲区的数据结构mdlsup.c

推荐阅读Windows Internals 7thPart 1 Chapter 9 “Memory Management”。


第二阶段:源码模块阅读(建议顺序)

2.1 核心头文件 — 建立全局认知

[miarm.h](file:///d:/reactos/ntoskrnl/mm/ARM3/miarm.h)
  • 地位:整个 MM 子系统的唯一核心头文件,零外部依赖(无#include
  • 阅读重点
    • 保护常量:MM_ZERO_ACCESSMM_EXECUTE_WRITECOPY
    • PTE 标志位:PTE_VALIDPTE_DIRTYPTE_ACCESSEDPTE_PROTOTYPEPTE_TRANSITION
    • PFN 状态:ZeroedPageListFreePageListStandbyPageListModifiedPageListBadPageList
    • 页表自映射区域:PTE_BASEPDE_BASE(x86 特有技巧)
    • 结构体:MMPFNMMPTEMMVADMM_AVL_TABLE
  • 目标:能闭眼画出 PFN 条目布局和 PTE 各字段含义
[miavl.h](file:///d:/reactos/ntoskrnl/mm/ARM3/miavl.h)
  • 地位:VAD 树专用的 AVL 算法内联实现,仅被vadnode.c包含
  • 阅读重点:理解从 RTL 通用 AVL 到 MM 专用类型的宏映射
  • 目标:理解平衡因子和父指针共用同一字段的优化

2.2 初始化子系统 — 理解启动流程

[ARM3/mminit.c](file:///d:/reactos/ntoskrnl/mm/ARM3/mminit.c)
  • 函数MmArmInitSystem
  • 阅读重点
    • Phase0 初始化:扫描内存描述符 → 构建 PFN 数据库 → 映射 PFN DB → 构建分页池 → 初始化颜色表
    • Phase1 初始化:已加载模块链表 → 大页面支持 → 特殊池 → 后台交换线程
    • 非分页池大小计算算法
  • 配合阅读:[mminit.c(旧版)](file:///d:/reactos/ntoskrnl/mm/mminit.c) 了解MmInitSystem
[amd64/init.c](file:///d:/reactos/ntoskrnl/mm/amd64/init.c) / [i386/init.c](file:///d:/reactos/ntoskrnl/mm/ARM3/i386/init.c)
  • 函数MiInitMachineDependent
  • 阅读重点:架构相关的模板 PTE/PDE 定义、页表层次映射

2.3 物理内存管理 — 页帧与分配器

[pfnlist.c](file:///d:/reactos/ntoskrnl/mm/ARM3/pfnlist.c)
  • 核心函数MiRemoveAnyPageMiRemoveZeroPageMiInsertPageInListMiInitializePfn
  • 阅读重点
    • 物理页的六种状态:Active / Transition / Standby / Modified / Free / Zeroed / Bad
    • 按颜色分配算法 (Cache Color)
    • 引用计数和共享计数的增减逻辑
  • 流程图:绘制物理页生命周期状态机
[freelist.c](file:///d:/reactos/ntoskrnl/mm/freelist.c)
  • 阅读重点:全局统计变量MmAvailablePagesMmTotalCommittedPagesMmPfnDatabase
[zeropage.c](file:///d:/reactos/ntoskrnl/mm/ARM3/zeropage.c)
  • 核心函数MiZeroPageThread
  • 阅读重点:后台零页线程的工作循环(从备用列表取页 → 置零 → 放入零页列表)
[contmem.c](file:///d:/reactos/ntoskrnl/mm/ARM3/contmem.c) / [largepag.c](file:///d:/reactos/ntoskrnl/mm/ARM3/largepag.c)
  • 阅读重点MmAllocateContiguousMemorySpecifyCache的完整分配路径
  • 理解:大页面的分配条件和对性能的影响

2.4 虚拟内存管理 — VAD 与系统调用

[vadnode.c](file:///d:/reactos/ntoskrnl/mm/ARM3/vadnode.c)
  • 核心函数MiLocateVadMiInsertVadMiRemoveNodeMiFindEmptyAddressRangeInTree
  • 阅读重点:在 AVL 树中查找包含指定地址的 VAD 的算法
  • 练习:手写MiLocateVad的二叉树搜索过程
[virtual.c](file:///d:/reactos/ntoskrnl/mm/ARM3/virtual.c)
  • 核心函数NtAllocateVirtualMemoryNtFreeVirtualMemoryNtProtectVirtualMemoryNtQueryVirtualMemory
  • 阅读重点(按顺序):
    1. NtAllocateVirtualMemory完整路径:参数验证 → 锁定工作集锁 → 找空闲区间 → 插 VAD → 提交页面
    2. NtFreeVirtualMemory路径:释放 VAD → 删除 PTE → 释放物理页 → 更新 PFN
    3. MmGetPhysicalAddress:虚拟地址 → 物理地址转换
    4. MiDeletePteMiDeleteVirtualAddresses:反向操作的底层逻辑
  • 数据流:画出 NtAllocateVirtualMemory 的完整函数调用链
[syspte.c](file:///d:/reactos/ntoskrnl/mm/ARM3/syspte.c)
  • 核心函数MiReserveSystemPtesMiReleaseSystemPtes
  • 阅读重点:系统 PTE 的位图分配算法

2.5 缺页异常处理 — 核心性能路径

[pagfault.c](file:///d:/reactos/ntoskrnl/mm/ARM3/pagfault.c)
  • 核心函数MmArmAccessFaultMiDispatchFault
  • 四种故障类型(按重要性排序):
    1. MiResolveDemandZeroFault— 请求零页:分配 → 置零 → 映射
    2. MiResolvePageFileFault— 页文件换回:解析 PTE 交换条目 → 磁盘 I/O 读回
    3. MiResolveTransitionFault— 转换故障:备用/已修改列表中的页重新激活
    4. MiResolveProtoPteFault— 原型 PTE 故障:共享页的写时复制 (COW)
  • 阅读重点
    • 错误码分析(PF 错误码各位含义)
    • MiCheckForUserStackOverflow— 用户栈自动扩展
    • MiAccessCheck— 访问权限验证
  • 配合阅读:[i386/page.c](file:///d:/reactos/ntoskrnl/mm/i386/page.c) 中页表操作底层函数

2.6 池分配器 — 内核堆

[expool.c](file:///d:/reactos/ntoskrnl/mm/ARM3/expool.c) + [pool.c](file:///d:/reactos/ntoskrnl/mm/ARM3/pool.c)
  • 核心函数ExAllocatePoolWithTagExFreePoolWithTag
  • 阅读重点
    • 三种分配粒度:小(POOL_DESCRIPTOR 列表)、中(位图管理页)、大(整页)
    • 池头部结构和标签跟踪机制
    • 热标签缓存 (Hot Tags)
    • 配额管理(MmRaisePoolQuota/MmReturnPoolQuota
    • 非分页池初始化流程
  • 配合阅读:[special.c](file:///d:/reactos/ntoskrnl/mm/ARM3/special.c) 了解 Driver Verifier 特殊池的填充模式检测

2.7 段对象与文件映射

[section.c(ARM3)](file:///d:/reactos/ntoskrnl/mm/ARM3/section.c)
  • 核心函数NtCreateSectionNtMapViewOfSection
  • 阅读重点
    • 数据文件映射(MiCreateDataFileMap)vs 分页文件映射(MiCreatePagingFileMap
    • 控制区 (Control Area) → 段 (Segment) → 原型 PTE (Prototype PTE) 的层次关系
    • 系统空间映射(MiMapViewInSystemSpace
  • 配合阅读:[mdlsup.c](file:///d:/reactos/ntoskrnl/mm/ARM3/mdlsup.c) 了解 MDL 创建和页面锁定

2.8 进程与会话

[procsup.c](file:///d:/reactos/ntoskrnl/mm/ARM3/procsup.c)
  • 核心函数MmCreateProcessAddressSpaceMmCleanProcessAddressSpaceMmDeleteProcessAddressSpace
  • 阅读重点:进程地址空间创建的完整步骤
  • 配合阅读:[i386/procsup.c](file:///d:/reactos/ntoskrnl/mm/ARM3/i386/procsup.c) / [amd64/procsup.c](file:///d:/reactos/ntoskrnl/mm/amd64/procsup.c) 理解架构差异
[session.c](file:///d:/reactos/ntoskrnl/mm/ARM3/session.c)
  • 阅读重点MmSessionCreate/MmSessionDelete、Session ID 位图管理

2.9 工作集与内存平衡

[wslist.cpp](file:///d:/reactos/ntoskrnl/mm/ARM3/wslist.cpp)
  • 核心函数MmWorkingSetManagerMiInsertInWorkingSetListMiRemoveFromWorkingSetList
  • 注意:这是 MM 目录中唯一的 C++ 文件
[balance.c](file:///d:/reactos/ntoskrnl/mm/balance.c)
  • 核心函数MiBalancerThreadMmTrimUserMemoryMmRebalanceMemoryConsumers
  • 阅读重点:页面回收的完整路径:
    1. 消费者注册和优先级
    2. Trim 回调调用
    3. 解除映射 → 写交换文件 → 放入备用列表

2.10 页面文件与反向映射

[pagefile.c](file:///d:/reactos/ntoskrnl/mm/pagefile.c)(旧版,未适配 ARM3)
  • 核心函数MmWriteToSwapPageMmReadFromSwapPageMmAllocSwapPageMmFreeSwapPage
  • 阅读重点:交换槽位分配和磁盘 I/O 发起的完整路径
[rmap.c](file:///d:/reactos/ntoskrnl/mm/rmap.c)(旧版)
  • 阅读重点:反向映射如何支持页面换出

2.11 系统镜像加载

[sysldr.c](file:///d:/reactos/ntoskrnl/mm/ARM3/sysldr.c)
  • 核心函数MmLoadSystemImageMmUnloadSystemImage
  • 阅读重点
    • PE 文件的加载过程
    • 导入表解析(MiResolveImageReferences
    • Thunk 修正(MiSnapThunk
    • 驱动程序可分页性控制(MmPageEntireDriver/MmResetDriverPaging

第三阶段:深入理解与验证练习

3.1 代码阅读练习

练习目标文件要求
画出页表自映射结构i386/procsup.c解释 PD[1023]→PD、PTE_BASE 寻址原理
追踪页分配路径pfnlist.cvirtual.cNtAllocateVirtualMemory到物理页的完整链
画出 PFN 状态机pfnlist.c+freelist.c包含所有 6 种状态的转换条件和触发函数
分析写时复制pagfault.cMiResolveProtoPteFault写出 COW 的触发条件和页面分配逻辑
池分配链路expool.cpool.cpfnlist.cExAllocatePoolWithTag到物理页的完整链
缺页异常完整路径pagfault.c画出 CPU 异常 → 内核分发 → 各子处理器的分派树
工作集修剪过程balance.c+wslist.cpp从内存压力触发到页面回收的完整路径

3.2 编译调试练习

练习操作验证方法
添加 DPRINT1 日志在关键路径入口添加日志查看串口输出确认调用路径
触发缺页异常运行测试程序访问未提交内存串口观察MiDispatchFault日志
监控池分配ExAllocatePoolWithTag添加跟踪查看分配模式和标签
触发页面回收运行大内存消耗程序观察MiBalancerThread活动

3.3 阅读路线图

┌─────────────────────────────────────────────────────────────────┐ │ 推荐阅读顺序 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. miarm.h + miavl.h ← 头文件基础(1天) │ │ ↓ │ │ 2. mminit.c ← 初始化流程(1天) │ │ ↓ │ │ 3. pfnlist.c + freelist.c ← 物理页管理(1-2天) │ │ ↓ │ │ 4. vadnode.c ← VAD 树算法(1天) │ │ ↓ │ │ 5. virtual.c ← 虚拟内存核心(2天) │ │ ↓ │ │ 6. pagfault.c + i386/page.c ← 缺页处理(2天) │ │ ↓ │ │ 7. expool.c + pool.c ← 池分配器(1-2天) │ │ ↓ │ │ 8. section.c(ARM3) ← 段对象(2天) │ │ ↓ │ │ 9. procsup.c + session.c ← 进程/会话(1天) │ │ ↓ │ │ 10. wslist.cpp + balance.c ← 工作集(1-2天) │ │ ↓ │ │ 11. pagefile.c + rmap.c ← 页文件/反向映射(1天) │ │ ↓ │ │ 12. sysldr.c ← 系统加载器(2天) │ │ ↓ │ │ 13. mdlsup.c + iosup.c ← MDL/I/O 支持(1天) │ │ ↓ │ │ 14. 架构文件(i386/amd64等) ← 架构差异(1天) │ │ │ │ 总计:约 17-22 天 │ └─────────────────────────────────────────────────────────────────┘

第四阶段:推荐学习资料

书籍

  1. Windows Internals, 7th Edition— Part 1, Chapter 9 “Memory Management”
    • 这是理解 Windows 内存管理架构的最佳参考
  2. Understanding the Linux Kernel, 3rd Edition— Chapter 8 “Memory Management”
    • 与 Linux 对比学习有助于加深理解
  3. Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A
    • 分页机制硬件细节

在线资源

  1. ReactOS 官方 Wiki — Memory Manager
  2. Windows内核原理与实现(潘爱民) — 中文经典
  3. WRK (Windows Research Kernel) 源码 — 对照参考

ReactOS 内部文档

  • doc/ARM3内存管理器分析.md— 已有的 ARM3 分析文档
  • doc/MM实现分析.md— 旧版 MM 的实现分析
  • doc/ntoskrnl内存管理器MM分析.md— 综合分析(已有)

第五阶段:学习验证清单

每节完成后自测

  • 能画出 x86 两级分页地址转换图
  • 能解释 PAE 和非 PAE 的区别
  • 能写出 x86-64 四级页表的各级名称
  • 能画出 PFN 条目结构和页面状态机
  • 能描述 VAD AVL 树的搜索算法
  • 能写出NtAllocateVirtualMemory的完整调用链
  • 能解释四种缺页故障类型及处理方式
  • 能描述池分配器的小/中/大三级分配策略
  • 能画出 Section 对象 → 控制区 → 段 → 原型 PTE 的层次
  • 能解释页表自映射技巧的原理
  • 能描述页面回收的完整路径(平衡器 → 修剪 → 换出)
  • 能解释逆向映射的作用和工作原理
  • 能描述 PE 镜像加载过程的关键步骤

代码修改练习

  • 在某个关键路径上添加 DPRINT1 并验证串口输出
  • 找到一处UNIMPLEMENTED或存根函数并实现它
  • 给某个复杂函数添加中文注释解释