ReactOS ntoskrnl/mm 内存管理器实现分析
ReactOS ntoskrnl/mm 内存管理器实现分析分析日期2026-07-11一、目录结构总览ntoskrnl/mm/目录包含两套内存管理器旧MM基于 MEMORY_AREA和 ARM3 新MM基于 MMVAD AVL 树以及跨架构的平台支持代码。1.1 目录布局ntoskrnl/mm/ ├── ARM3/ # 新内存管理器ARM3BSD协议 │ ├── miarm.h # 主头文件关键常量、宏、内联函数 │ ├── miavl.h # AVL 树相关头文件 │ ├── mminit.c # Phase 0/1 初始化 │ ├── pagfault.c # 缺页故障处理核心 │ ├── section.c # 节区对象NtCreateSection 等 │ ├── virtual.c # 虚拟内存 APINtAllocateVirtualMemory 等 │ ├── sysldr.c # 内核加载器PE 文件加载 │ ├── pool.c # 池分配器底层页级分配/释放 │ ├── expool.c # 池分配器上层lookaside、分片 │ ├── pfnlist.c # PFN 数据库管理 │ ├── procsup.c # 进程地址空间创建/销毁 │ ├── vadnode.c # VAD AVL 树操作 │ ├── wslist.cpp # 工作集列表管理C │ ├── contmem.c # 连续物理内存分配 │ ├── session.c # 会话空间管理 │ ├── syspte.c # 系统 PTE 管理 │ ├── zeropage.c # 零页线程 │ ├── drvmgmt.c # 驱动管理 │ ├── hypermap.c # 超空间映射 │ ├── iosup.c # I/O 支持函数 │ ├── largepag.c # 大页面支持 │ ├── mdlsup.c # MDL 支持 │ ├── mmsup.c # MM 辅助函数 │ ├── ncache.c # 非缓存内存 │ ├── special.c # 特殊用途页面 │ ├── kdbg.c # 内核调试器支持 │ ├── mmdbg.c # MM 调试支持 │ ├── dynamic.c # 动态内存管理 │ ├── i386/init.c # x86 平台 ARM3 初始化 │ └── arm/init.c # ARM 平台 ARM3 初始化 ├── marea.c # [旧MM] 内存区域管理 ├── mmfault.c # [分派层] 缺页故障入口 ├── section.c # [分派层] 节区对象 ├── mminit.c # [旧MM] 初始化 ├── rmap.c # [旧MM] 反向映射 ├── pagefile.c # [旧MM] 页面文件 ├── region.c # [旧MM] 区域管理 ├── balance.c # [旧MM] 内存平衡器 ├── freelist.c # [旧MM] 空闲列表 ├── shutdown.c # [旧MM] 关闭清理 ├── i386/page.c # x86 页面管理 ├── i386/pagepae.c # x86 PAE 页面管理 ├── i386/procsup.c # x86 进程支持 ├── amd64/init.c # amd64 平台初始化 ├── amd64/procsup.c # amd64 进程支持 ├── arm/page.c # ARM 页面管理 └── arm/stubs.c # ARM 存根函数1.2 文件数量统计类别数量ARM3 源文件28旧MM 源文件7分派层文件2平台支持7总计44二、架构概述双内存管理器共存ReactOS 内核内存管理器采用渐进式替换策略旧MM 和 ARM3 通过分派层共存。系统调用层NtAllocateVirtualMemory, NtCreateSection, NtMapViewOfSection... │ ▼ 分派层MmAccessFault, MmMapViewOfSection, MmCreateSection... │ │ │ MiIsRosSectionObject│ / MI_IS_ROSMM_VAD 检查 ▼ ▼ ┌─────┐ ┌─────────┐ │旧MM │ │ ARM3 │ │路径 │ │ 路径 │ └─────┘ └─────────┘ MEMORY_AREA MMVAD AVL 树 Linux-like WRK 兼容 GPL 协议 BSD 协议2.1 分派层判断逻辑mmfault.c缺页分派// ARM3 路径条件// 1. 地址是共享用户数据 或 页表地址// 2. 找到的 VAD 不是旧MM VAD (!MI_IS_ROSMM_VAD(Vad))if(/* 条件满足 */)returnMmArmAccessFault(...);// - ARM3 pagfault.celsereturnMmpAccessFault(...);// - 旧MM mmfault.csection.c节区对象分派if(MiIsRosSectionObject(SectionObject)FALSE)returnMmMapViewOfArm3Section(...);// - ARM3// 否则走旧MM路径2.2 关键区分标志标志位置含义MI_IS_ROSMM_VAD(Vad)internal/mm.hVAD 属于旧MMMI_IS_MEMORY_AREA_VAD(Vad)internal/mm.hVAD 是 MEMORY_AREA旧MMMiIsRosSectionObject(Section)miarm.hSection 属于旧MMMEMORY_AREA_OWNED_BY_ARM3internal/mm.h旧MM 中标记为 ARM3 拥有的区域三、ARM3 核心数据结构3.1 VADVirtual Address Descriptor -miarm.hVAD 是 ARM3 管理虚拟地址空间的核心数据结构。每个 VAD 描述一段连续的虚拟地址范围。// MMVAD基础 VADtypedefstruct_MMVAD{MMADDRESS_NODE VadNode;// AVL 树节点StartingVpn, EndingVpnULONG_PTR StartingVpn;// 起始虚拟页号ULONG_PTR EndingVpn;// 结束虚拟页号union{MMVAD_FLAGS VadFlags;// VAD 标志ULONG LongFlags;}u;union{MMVAD_FLAGS VadFlags2;// 扩展标志ULONG LongFlags2;}u2;...}MMVAD,*PMMVAD;// MMVAD_LONG长 VAD用于节区映射typedefstruct_MMVAD_LONG{MMVAD Vad;// 基础 VADPCONTROL_AREA ControlArea;// 控制区域指向节区...}MMVAD_LONG,*PMMVAD_LONG;VAD 使用AVL 树RTL AVL 树实现组织支持 O(log n) 的查找、插入和删除。vadnode.c封装了所有 VAD 树操作。3.2 CONTROL_AREA / SEGMENT / SUBSECTION -miarm.h节区对象的三层结构SECTION对象管理器对象 │ └── SEGMENT物理内存描述 │ ├── CONTROL_AREA控制信息 │ ├── FilePointer文件对象 │ └── FlagsGlobalOnlyPerSession, Rom... │ └── SUBSECTION子节区紧跟在 ControlArea 后 ├── SubsectionBase原型 PTE 数组 ├── PtesInSubsectionPTE 数量 └── NextSubsection下一个子节区链表3.3 PFNPage Frame Number数据库 -miarm.h物理页描述符数组通过MmPfnDatabase全局指针访问以页面帧号索引。typedefstruct_MMPFN{union{PFN_NUMBER Flink;// 链表前驱 / WsIndex / EventULONG WsIndex;PKEVENT Event;NTSTATUS ReadStatus;}u1;PMMPTE PteAddress;// 指向映射此页的 PTEunion{PFN_NUMBER Blink;// 链表后继 / ShareCountULONG_PTR ShareCount;}u2;union{struct{USHORT ReferenceCount;// 引用计数MMPFNENTRY e1;// 页状态Modified, ReadInProgress...};}u3;MMPTE OriginalPte;// 原始 PTE 值union{ULONG_PTR PteFrame:25;// 页表页的页帧号...}u4;}MMPFN,*PMMPFN;3.4 保护模式常量 -miarm.h// ARM3 内部保护掩码3位#defineMM_ZERO_ACCESS0// 零访问#defineMM_READONLY1// 只读#defineMM_EXECUTE2// 仅执行#defineMM_EXECUTE_READ3// 执行读#defineMM_READWRITE4// 读写#defineMM_WRITECOPY5// 写复制#defineMM_EXECUTE_READWRITE6// 执行读写#defineMM_EXECUTE_WRITECOPY7// 执行写复制// 特殊标志叠加在保护掩码之上#defineMM_NOCACHE0x08// 禁止缓存#defineMM_GUARDPAGE0x10// 守卫页#defineMM_WRITECOMBINE0x18// 写合并3.5 页面列表类型 -internal/mm.htypedefenum_MI_PFN_USAGES{MI_USAGE_NOT_SET0,MI_USAGE_PAGED_POOL,// 分页池MI_USAGE_NONPAGED_POOL,// 非分页池MI_USAGE_KERNEL_STACK,// 内核栈MI_USAGE_SYSTEM_PTE,// 系统 PTEMI_USAGE_VAD,// VADMI_USAGE_SECTION,// 节区MI_USAGE_PAGE_TABLE,// 页表MI_USAGE_PAGE_DIRECTORY,// 页目录MI_USAGE_DRIVER_PAGE,// 驱动页面MI_USAGE_DEMAND_ZERO,// 请求调零MI_USAGE_COW,// 写时复制MI_USAGE_FREE_PAGE,// 空闲页面...}MI_PFN_USAGES;四、ARM3 核心实现流程4.1 系统初始化流程Phase 0mminit.c: MiInitMachineDependent ├── MmInitializeMemoryLimits # 计算物理内存上下限 ├── MiComputeKernelAddressRange # 计算内核地址范围 ├── MiInitializeColorTables # 初始化着色表 ├── MiCreateArm3StaticMemoryAreas # 创建静态内存区域 └── MiComputePagedPoolQuota # 计算分页池配额 Phase 1mminit.c: MmInitSystem ├── MiInitMachineDependent ├── MiCreatePagedPool # 创建分页池 ├── MiCreateNonPagedPool # 创建非分页池 ├── MiInitializeSystemPte # 初始化系统 PTE ├── MiInitializeWorkingSetList # 初始化工作集链表 ├── MiInitializeProcessors # 初始化处理器 └── MiInitializeLoadedModuleList # 初始化已加载模块列表sysldr.c4.2 缺页处理流程最核心路径#PF缺页异常 │ ▼ KiTrap0E → MmAccessFault分派层mmfault.c │ ├── ARM3 路径MmArmAccessFaultpagfault.c │ │ │ ├── 内核缺页Address MmSystemRangeStart │ │ ├── 锁工作集 → 读 PTE │ │ ├── PTE 有效 → 检查 COW/只读/NX │ │ ├── Prototype PTE → 解析原型 PTE │ │ └── MiDispatchFault底层分发 │ │ │ └── 用户缺页Address MmSystemRangeStart │ ├── 锁进程工作集 → 读 PTE │ ├── 检查页目录PXE/PPE/PDE有效性 │ ├── PTE 状态判断 │ │ ├── 有效 COW → 复制页 │ │ ├── Demand Zero → MiResolveDemandZeroFault │ │ ├── 零 PTE → MiCheckVirtualAddress 查 VAD │ │ ├── Prototype → 解析原型 PTE 再调度 │ │ └── Transition → MiResolveTransitionFault │ └── MiDispatchFault底层分发 │ └── 旧MM 路径MmpAccessFaultmmfault.c └── MmLocateMemoryAreaByAddress → 按类型分发 MiDispatchFault底层缺页分发pagfault.c: │ ├── 有原型 PTE → MiResolveProtoPteFault │ ├── COW 场景分配新页 → 复制 → 更新原型 PTE │ ├── Transition 场景处理过渡页 │ ├── Demand Zero 场景分配零页 │ └── Pagefile 场景从页面文件读入 │ ├── Transition PTE → MiResolveTransitionFault │ └── 从空闲/零链表移除 → 增加共享计数 → 写有效 PTE │ ├── Pagefile PTE → MiResolvePageFileFault │ └── 分配页 → 构建 Transition PTE → MiReadPageFile │ └── Demand Zero PTE → MiResolveDemandZeroFault └── 获取零页/空闲页 → 初始化 PFN → 写有效 PTE4.3 虚拟内存分配流程NtAllocateVirtualMemoryvirtual.c │ ├── MEM_RESERVE保留地址空间 │ ├── 分配 MMVAD_LONG 结构 │ ├── 设置 StartingVpn / EndingVpn │ └── MiInsertVadEx 插入 AVL 树 │ └── MEM_COMMIT提交物理页 ├── 锁定地址空间 ├── 查找 VADMiCheckForConflictingNode ├── Section VAD写入 Segment 模板 PTE └── 私有 VAD ├── 更新 CommitCharge ├── 锁工作集 ├── MiMakePdeExistAndMakeValid 确保 PDE 有效 └── 循环写入 Demand Zero 的无效 PTE4.4 内存映射文件流程MmMapViewOfSection分派层 section.c │ ├── ARM3 路径MmMapViewOfArm3Section │ ├── 计算视图大小和对齐 │ ├── 查找/创建 VADMiInsertVadEx │ ├── 更新 VAD 的 ControlArea 引用 │ ├── 写入原型 PTE从 Segment 获取 │ └── 返回映射基址 │ └── 旧MM 路径 ├── 锁定地址空间 ├── IMAGE 节区循环映射每个 Segment └── 数据节区MmMapViewOfSegment4.5 驱动程序加载流程MmLoadSystemImagesysldr.c │ ├── 获取加载锁MmSystemLoadLock ├── 扫描模块列表PsLoadedModuleList ├── 打开文件ZwOpenFile ├── 检查镜像MmCheckSystemImage ├── 创建 SectionZwCreateSection, SEC_IMAGE │ ├── MiLoadImageSection │ ├── 附加到系统进程 │ ├── MiChargeResidentAvailable检查可用内存 │ ├── MiReserveSystemPtes保留系统 PTE │ ├── MiChargeCommitment增加提交计数 │ ├── MmMapViewOfSection映射临时视图 │ ├── 循环 │ │ ├── MiAllocatePfn分配物理页 │ │ ├── MI_WRITE_VALID_PTE写入有效 PTE │ │ └── RtlCopyMemory从视图复制数据 │ ├── ZwUnmapViewOfSection解除临时映射 │ ├── MmPurgeSection清除备用列表 │ └── 返回基址 │ ├── 大页面优化MiUseLargeDriverPage ├── 重定位LdrRelocateImageWithBias ├── 分配 LDR_DATA_TABLE_ENTRY ├── 解析导入依赖 ├── 插入模块列表 ├── 缓存调试符号 └── 释放加载锁4.6 池分配流程ExAllocatePoolWithTagexpool.c │ ├── 大分配 POOL_MAX_ALLOC │ └── MiAllocatePoolPages 直接分配页 │ └── 小分配 ├── Lookaside 快速路径 ├── 自由链表搜索 │ ├── 找到合适块 → 分片 → 返回 │ └── 未找到 → MiAllocatePoolPages 分配新页 └── 新页分片 → 剩余块插入自由链表 MiAllocatePoolPagespool.c │ ├── Paged Pool │ ├── S-List 快速路径1页 │ ├── 位图分配PagedPoolAllocationMap │ └── Paged Pool 扩展新页表 │ └── NonPaged Pool ├── S-List 快速路径1页 ├── 自由链表4个按页数分桶 └── NonPaged Pool 扩展保留系统 PTE4.7 进程地址空间生命周期MmCreateProcessAddressSpaceprocsup.c └── 分配页目录 超空间页表 工作集页 MmInitializeProcessAddressSpaceprocsup.c ├── 初始化 VAD 树和锁 ├── 初始化 PDE/超空间/工作集的 PFN ├── 初始化工作集链表 └── 映射节区视图如有 MmCleanProcessAddressSpaceprocsup.c ├── 枚举所有 VAD ├── 旧MM VAD → MiRosCleanupMemoryArea └── ARM3 VAD → MiRemoveMappedView / MiDeleteVirtualAddresses MmDeleteProcessAddressSpaceprocsup.c ├── 删除工作集页和页表页 ├── 删除超空间页 ├── 删除页目录 └── 释放会话引用五、关键文件一览5.1 ARM3 核心层文件核心函数职责pagfault.cMmArmAccessFault,MiDispatchFault缺页处理section.cMmCreateArm3Section,MmMapViewOfArm3Section节区对象virtual.cNtAllocateVirtualMemory,NtProtectVirtualMemory虚拟内存 APIsysldr.cMmLoadSystemImage,MiLoadImageSectionPE 加载pool.cMiAllocatePoolPages,MiFreePoolPages页级池分配expool.cExAllocatePoolWithTag,ExFreePoolWithTag池分配器pfnlist.cMiAllocatePfn,MiFreePfnPFN 管理procsup.cMmCreateProcessAddressSpace,MmCleanProcessAddressSpace进程地址空间vadnode.cMiInsertVad,MiLocateVad,MiFindEmptyAddressRangeInTreeVAD 树操作mminit.cMmInitSystem,MiInitMachineDependent初始化zeropage.cMiZeroPageThread零页线程wslist.cppMiInsertWsle,MiRemoveWsle工作集列表5.2 分派层文件核心函数职责mmfault.cMmAccessFault,MmpAccessFault缺页分派section.cMmCreateSection,MmMapViewOfSection节区分派5.3 旧MM文件核心函数职责marea.cMmCreateMemoryArea,MmFreeMemoryArea,MmLocateMemoryAreaByAddress内存区域mmfault.cMmAccessFaultSectionView,MmNotPresentFaultSectionView旧缺页处理rmap.cMmPageOutPhysicalAddress反向映射pagefile.c页面文件操作页面文件5.4 头文件文件内含职责miarm.h~2560 行所有 ARM3 宏、常量、内联函数、数据结构定义、extern 声明mi.h~440 行WRK 兼容性头包装 miarm.h提供 WRK 风格宏internal/mm.h~1480 行MM 内部公共头PFN 结构、页面列表、函数声明internal/i386/mm.hx86 架构 MM 定义架构相关的 PTE/PDE 操作六、小结ReactOS 的内存管理器实现体现了典型的渐进式架构迁移设计双MM共存旧MMMEMORY_AREA和 ARM3MMVAD通过分派层并行运行ARM3 正在逐步取代旧MM。WRK 兼容ARM3 的设计与 Windows Research Kernel (WRK) v1.2 高度一致包括数据结构命名MMVAD、CONTROL_AREA、MMPFN、函数命名MiResolveProtoPteFault、MiAllocatePfn和整体架构。分阶段初始化系统初始化分为 Phase 0早期、INIT 代码段和 Phase 1完整初始化、可分页与 NT 内核设计一致。分层抽象从硬件 PTE/PDE → PFN 数据库 → VAD 树 → 系统调用 API每一层职责清晰。性能优化Lookaside 列表池分配、S-List单页池、Hint 缓存VAD 查找、着色表页面缓存优化等设计体现了对性能的考虑。