NTOSKRNL 内存管理器(MM)分析
概述
ReactOS 内核的内存管理器(Memory Manager,简称 MM)位于ntoskrnl\mm\目录。它由两套子系统组成:
- 旧版 MM(
ntoskrnl\mm\*.c,根目录):ReactOS 早期实现的独立内存管理器 - ARM3 MM(
ntoskrnl\mm\ARM3\*.c):后来引入的、对齐 Windows NT 架构的新内存管理器,目前是主力实现
两套子系统并存,部分功能由 ARM3 覆盖,部分仍由旧版提供。架构相关代码分离在i386/、amd64/、arm/子目录中。
1. 目录文件清单与作用
1.1 根目录文件(Legacy / 旧版 MM)
| 文件 | 功能 |
|---|
| [balance.c](file:///d:/reactos/ntoskrnl/mm/balance.c) | 内存平衡器— 后台内核线程周期性修剪各内存消费者的页面,回收物理页面到空闲列表。MmTrimUserMemory遍历用户 LRU 链表进行换出。 |
| [freelist.c](file:///d:/reactos/ntoskrnl/mm/freelist.c) | 空闲页面链表与统计— 管理 PFN 数据库、MmAvailablePages、MmTotalCommittedPages等全局物理内存账本。 |
| [marea.c](file:///d:/reactos/ntoskrnl/mm/marea.c) | 内存区域管理— 虚拟地址区域(VAD)的创建、插入 AVL 树、删除。提供MmCreateMemoryArea/MmInsertMemoryArea/MmFreeMemoryArea。 |
| [mmfault.c](file:///d:/reactos/ntoskrnl/mm/mmapfault.c) | 缺页异常处理(旧版) —MmAccessFault根据错误码分派到MmNotPresentFault等处理程序。 |
| [mminit.c](file:///d:/reactos/ntoskrnl/mm/mminit.c) | 内存管理器初始化(旧版) —MmInitSystem分 Phase0/Phase1 两阶段执行,建立内核地址空间和系统内存区域。 |
| [pagefile.c](file:///d:/reactos/ntoskrnl/mm/pagefile.c) | 页面文件管理— 交换文件的读写 I/O、交换槽位的分配/释放。MmWriteToSwapPage/MmReadFromSwapPage。 |
| [region.c](file:///d:/reactos/ntoskrnl/mm/region.c) | 区域子划分— 在内存区域内进一步划分子区域,支持按地址范围修改属性和查找。 |
| [rmap.c](file:///d:/reactos/ntoskrnl/mm/rmap.c) | 反向映射— 通过物理页面找到所有映射到它的虚拟地址。页面换出的关键前置步骤。 |
| [section.c](file:///d:/reactos/ntoskrnl/mm/section.c) | 段对象(旧版) — 内存映射文件(Section)的创建、视图映射/取消映射。 |
| [shutdown.c](file:///d:/reactos/ntoskrnl/mm/shutdown.c) | 关闭清理— 系统关闭时释放页面文件资源、同步脏页面。 |
1.2 ARM3 子系统(ARM3/目录)
ARM3 是当前的主力内存管理器实现,采用分层架构,各文件职责如下:
初始化与基础设施
| 文件 | 功能 |
|---|
| [mminit.c](file:///d:/reactos/ntoskrnl/mm/ARM3/mminit.c) | ARM3 初始化核心—MmArmInitSystem两阶段初始化。扫描 LoaderBlock 内存描述符、构建 PFN 数据库、映射 PFN 数据库、构建分页池、初始化颜色表、内存事件等。 |
| [miarm.h](file:///d:/reactos/ntoskrnl/mm/ARM3/miarm.h) | 主头文件— 核心数据结构定义、宏(页面大小、PTE/PD 尺寸、最低 VAD 地址等)。 |
| [miavl.h](file:///d:/reactos/ntoskrnl/mm/ARM3/miavl.h) | AVL 树头文件— VAD 节点专用的 AVL 树算法宏和类型映射。 |
| [sysldr.c](file:///d:/reactos/ntoskrnl/mm/ARM3/sysldr.c) | 系统镜像加载器(~3600行)— PE 文件的加载/卸载、导入表解析、Thunk 修正、已加载模块链表管理、驱动程序可分页性控制。 |
| [procsup.c](file:///d:/reactos/ntoskrnl/mm/ARM3/procsup.c) | 进程内存空间管理— PEB/TEB 创建删除、内核栈管理、进程地址空间创建/清理/删除、AWE(Address Windowing Extensions)支持。 |
物理内存管理
| 文件 | 功能 |
|---|
| [pfnlist.c](file:///d:/reactos/ntoskrnl/mm/ARM3/pfnlist.c) | PFN 列表操作— 页面在空闲/备用/已修改/置零列表间的移动、按颜色分配、引用计数/共享计数管理、页面清零。 |
| [freelist.c](file:///d:/reactos/ntoskrnl/mm/freelist.c) | 空闲页管理(旧版)— 提供全局物理页统计(旧版 MM 仍在使用)。 |
| [zeropage.c](file:///d:/reactos/ntoskrnl/mm/ARM3/zeropage.c) | 零页线程—MiZeroPageThread后台线程将已释放页面清零,维护零页列表供应。 |
| [contmem.c](file:///d:/reactos/ntoskrnl/mm/ARM3/contmem.c) | 连续内存分配—MmAllocateContiguousMemorySpecifyCache等 API,按最低/最高地址、边界对齐分配连续物理页。 |
| [dynamic.c](file:///d:/reactos/ntoskrnl/mm/ARM3/dynamic.c) | 动态物理内存— 热插拔内存支持(MmAddPhysicalMemory等),大部分尚未实现。 |
| [largepag.c](file:///d:/reactos/ntoskrnl/mm/ARM3/largepag.c) | 大页面支持— 2MB/4MB 页面分配、大页面范围管理、驱动大页面列表。 |
虚拟内存管理
| 文件 | 功能 |
|---|
| [virtual.c](file:///d:/reactos/ntoskrnl/mm/ARM3/virtual.c) | 虚拟内存核心(最大文件之一)—NtAllocateVirtualMemory/NtFreeVirtualMemory/NtProtectVirtualMemory/NtQueryVirtualMemory、跨进程内存复制、物理地址↔虚拟地址转换、安全虚拟内存、写时监视。 |
| [vadnode.c](file:///d:/reactos/ntoskrnl/mm/ARM3/vadnode.c) | VAD 树算法— AVL 树插入/删除/查找、空闲地址范围搜索、冲突检查。 |
| [syspte.c](file:///d:/reactos/ntoskrnl/mm/ARM3/syspte.c) | 系统 PTE 分配器— 系统 PTE 的预留/分配/释放/初始化,支持对齐分配。 |
| [hypermap.c](file:///d:/reactos/ntoskrnl/mm/ARM3/hypermap.c) | 超空间映射— 物理页面的临时虚拟映射(MiMapPageInHyperSpace/MiUnmapPageInHyperSpace),用于 MM 内部操作。 |
页面错误与工作集
| 文件 | 功能 |
|---|
| [pagfault.c](file:///d:/reactos/ntoskrnl/mm/ARM3/pagfault.c) | 页面错误处理(ARM3)—MmArmAccessFault入口、分派到 Demand Zero / PageFile / Transition / Proto PTE 各子处理、栈溢出检查、访问权限检查。 |
| [wslist.cpp](file:///d:/reactos/ntoskrnl/mm/ARM3/wslist.cpp) | 工作集列表管理— 进程工作集初始化、插入/移除页面、工作集管理器线程(MmWorkingSetManager)修剪逻辑。唯一 C++ 文件。 |
| [balance.c](file:///d:/reactos/ntoskrnl/mm/balance.c) | 内存平衡器(旧版)— 页面回收的后台线程。 |
池分配器
| 文件 | 功能 |
|---|
| [expool.c](file:///d:/reactos/ntoskrnl/mm/ARM3/expool.c) | Executive 池分配器—ExAllocatePoolWithTag/ExFreePoolWithTag、分页池/非分页池管理、大页表管理、池标签跟踪、热标签缓存、验证检查、配额管理。 |
| [pool.c](file:///d:/reactos/ntoskrnl/mm/ARM3/pool.c) | 池分配器底层页面管理— 池物理页面的分配/释放、非分页池初始化、已释放池页面保护、配额提升/归还。 |
| [special.c](file:///d:/reactos/ntoskrnl/mm/ARM3/special.c) | 特殊池(Driver Verifier)— 带填充模式的分配/释放、越界访问和 Use-After-Free 检测。 |
| [ncache.c](file:///d:/reactos/ntoskrnl/mm/ARM3/ncache.c) | 非缓存内存分配—MmAllocateNonCachedMemory/MmFreeNonCachedMemory。 |
Section(段对象)与 Session
| 文件 | 功能 |
|---|
| [section.c](file:///d:/reactos/ntoskrnl/mm/ARM3/section.c) | Section 对象(ARM3,~3500行)—NtCreateSection/NtMapViewOfSection、数据文件和分页文件映射、系统空间/会话空间视图管理、Segment 生命周期。 |
| [session.c](file:///d:/reactos/ntoskrnl/mm/ARM3/session.c) | Session 内存管理— Session 创建/删除/附加/分离、Session ID 位图管理、引用计数。 |
I/O 与调试支持
| 文件 | 功能 |
|---|
| [iosup.c](file:///d:/reactos/ntoskrnl/mm/ARM3/iosup.c) | I/O 空间映射—MmMapIoSpace/MmUnmapIoSpace、显示帧缓冲映射、平台缓存属性表。 |
| [mdlsup.c](file:///d:/reactos/ntoskrnl/mm/ARM3/mdlsup.c) | MDL 管理— MDL 创建/大小计算/页面分配释放/锁定/缓存指定/预取/保护。 |
| [kdbg.c](file:///d:/reactos/ntoskrnl/mm/ARM3/kdbg.c) | 调试器支持— Pool 使用统计查询等 KD 扩展。 |
| [mmdbg.c](file:///d:/reactos/ntoskrnl/mm/ARM3/mmdbg.c) | 内存调试— 物理地址翻译、安全内存复制供内核调试器使用。 |
| [drvmgmt.c](file:///d:/reactos/ntoskrnl/mm/ARM3/drvmgmt.c) | 驱动管理— 可分页驱动节锁定/解锁、Driver Verifier 支持。 |
| [mmsup.c](file:///d:/reactos/ntoskrnl/mm/ARM3/mmsup.c) | 杂项支持例程— 地址有效性检查、工作集大小调整、递归 I/O 故障检测、系统大小查询。 |
1.3 架构相关文件
| 文件 | 架构 | 功能 |
|---|
| [i386/init.c](file:///d:/reactos/ntoskrnl/mm/ARM3/i386/init.c) | x86-32 / ARM3 | 定义 x86 模板 PTE/PDE(ValidKernelPde/Pte、DemandZeroPde)、MiInitMachineDependent |
| [i386/page.c](file:///d:/reactos/ntoskrnl/mm/i386/page.c) | x86-32 / ARM3 | 核心页管理:映射创建/删除/查询、保护掩码表、PTE/PDE 操作、页面文件映射管理 |
| [i386/pagepae.c](file:///d:/reactos/ntoskrnl/mm/i386/pagepae.c) | x86-32 PAE(旧版) | PAE 模式页管理:TLB 刷新、进程地址空间创建(7页 vs 2页)、页表分配/释放 |
| [i386/procsup.c](file:///d:/reactos/ntoskrnl/mm/ARM3/i386/procsup.c) | x86-32 / ARM3 | MiArchCreateProcessAddressSpace— 创建进程地址空间(2级页表、自映射 PDE 技巧) |
| [amd64/init.c](file:///d:/reactos/ntoskrnl/mm/amd64/init.c) | x86-64 / ARM3 | MiInitMachineDependent— 定义 amd64 模板 PTE/PDE、初始化非分页池和系统 PTE 空间、构建 PFN 数据库(4级页表:PML4→PDPT→PD→PT) |
| [amd64/procsup.c](file:///d:/reactos/ntoskrnl/mm/amd64/procsup.c) | x86-64 / ARM3 | MiArchCreateProcessAddressSpace— 创建 amd64 进程地址空间(4级页表配置、自映射 PML4) |
| [arm/init.c](file:///d:/reactos/ntoskrnl/mm/ARM3/arm/init.c) | ARM / ARM3 | MiInitMachineDependent— 初始化非分页池大小和系统内存布局边界 |
| [arm/page.c](file:///d:/reactos/ntoskrnl/mm/arm/page.c) | ARM | 保护掩码表 + 大部分为 UNIMPLEMENTED 存根 |
| [arm/stubs.c](file:///d:/reactos/ntoskrnl/mm/arm/stubs.c) | ARM | 页表辅助函数,部分实现 |
2. 内存管理流程图
2.1 系统启动初始化流程
Phase0 初始化(MmArmInitSystem Phase=0) │ ├─ MiScanMemoryDescriptors() │ 从 LoaderBlock->MemoryDescriptorList 读取物理内存布局 │ 分类:可用 RAM、XIP ROM、坏块、固件保留等 │ ├─ MiComputeColorInformation() │ 计算 Cache Color(L2 Cache 大小 / 页面大小) │ 初始化颜色表 MiInitializeColorTables() │ ├─ MiBuildPfnDatabase() │ 为每个物理页创建 PFN 条目(约 24 字节/页) │ → 非分页池中为 PFN 数据库预留空间 │ ├─ MiMapPfnDatabase() │ 将 PFN 数据库映射到虚拟地址空间 │ ├─ 初始化系统 PTE 空间(MiInitializeSystemPtes / MiReserveSystemPtes) │ ├─ MiBuildPagedPool() / MiInitializeNonPagedPool() │ 构建分页池和非分页池 │ ├─ MiInitializeMemoryEvents() │ 初始化低内存/高内存事件 │ └─ 初始化工作集管理器参数(MiAdjustWorkingSetManagerParameters) Phase1 初始化(MmArmInitSystem Phase=1) │ ├─ MmInitSystem(旧版 MM 的 Phase1 初始化) │ ├─ MiInitializeLoadedModuleList() │ 建立已加载模块链表,记录所有内核驱动和 DLL │ ├─ MiReloadBootLoadedDrivers() │ 重新处理引导时加载的驱动导入表 │ ├─ MiBuildImportsForBootDrivers() │ 构建导入表解析 │ ├─ MiInitializeLargePageSupport() │ 初始化大页面分配支持 │ ├─ MiInitializeSpecialPool() │ 初始化 Driver Verifier 特殊池 │ └─ MmInitBsmThread() 初始化后台交换元数据线程
2.2 缺页异常处理流程
CPU 触发 Page Fault(CR2 = 故障地址) │ ▼ KiTrap0E(x86 缺页中断处理程序) │ ▼ MmAccessFault (旧版) / MmArmAccessFault (ARM3) │ ├─ 确定故障类型(错误码分析) │ ├─ 页面不存在(位0=0) │ ├─ 写操作违规(位1=1) │ ├─ 用户模式(位2=0) │ └─ 保留位违规(位3=1) │ ├─ MiCheckForUserStackOverflow() │ 检查是否为用户栈溢出 → 自动扩展栈 VAD │ ├─ MiDispatchFault() │ │ │ ├─ MiResolveDemandZeroFault() │ │ 请求置零故障:分配一个零页并映射 │ │ → 调用 MiRemoveZeroPage() 从零页列表取页 │ │ → 若空则从空闲列表取页并调用 MiZeroPhysicalPage() │ │ → 初始化 PFN 条目 │ │ → 填写 PTE │ │ │ ├─ MiResolvePageFileFault() │ │ 页面文件故障:从交换文件读回页面 │ │ → 解析 PTE 中的交换条目(页面文件号 + 槽位偏移) │ │ → MmReadFromSwapPage() → 发起磁盘 I/O │ │ → 等待 I/O 完成 │ │ → 将页面映射到虚拟地址 │ │ │ ├─ MiResolveTransitionFault() │ │ 转换故障:页面在备用/已修改列表中 │ │ → 从列表中移除 │ │ → 重新映射并更新 PFN 状态 │ │ │ ├─ MiResolveProtoPteFault() │ │ 原型 PTE 故障:多个进程共享的映射 │ │ → 复制原型 PTE │ │ → 若需写时复制(COW),分配新页面并复制内容 │ │ → 更新共享计数的 PFN │ │ │ └─ MiCompleteProtoPteFault() │ 完成原型 PTE 故障的后续处理 │ ├─ MiAccessCheck() │ 检查访问权限(读/写/执行) │ └─ 返回 STATUS_SUCCESS 或 引发访问违规异常
2.3 虚拟内存分配流程(NtAllocateVirtualMemory)
NtAllocateVirtualMemory(ProcessHandle, BaseAddress, ZeroBits, Size, AllocationType, Protect) │ ├─ 参数验证(大小对齐、保护标志合法性) │ ├─ MmCheckForContiguousMemory() — 特殊池检查 │ ├─ 锁定工作集锁(WorkingSetLock → AddressSpaceLock) │ ├─ 查找空闲地址范围 │ MiFindEmptyAddressRangeInTree() │ → 遍历 VAD AVL 树,查找满足大小和对齐的空闲区间 │ ├─ 预留(MEM_RESERVE) │ MiInsertVad() │ → 创建 VAD 节点(记录起始地址、大小、保护属性) │ → 插入进程的 VAD AVL 树 │ ├─ 提交(MEM_COMMIT) │ MiCommitPages() │ → 预留系统 PTE(如需要) │ → 更新已提交页面计数(MmTotalCommittedPages) │ → 创建或更新页表项(填写 DemandZero PTE) │ ├─ 释放工作集锁 │ └─ 返回 STATUS_SUCCESS
2.4 页面回收流程(内存平衡器)
内存压力触发(MmAvailablePages 低于阈值) │ ▼ MmRebalanceMemoryConsumers() │ ▼ MiBalancerThread(平衡器内核线程主循环) │ └─ 循环: │ ├─ 计算各消费者应修剪的页面数 │ → 根据消费者优先级和目标值分配配额 │ ├─ MiTrimMemoryConsumer(Consumer) │ │ │ ├─ 调消费者注册的 Trim 回调 │ │ → 对用户进程:MmTrimUserMemory(遍历 LRU 链表) │ │ → 对系统缓存:调用缓存的修剪函数 │ │ │ ├─ 对每个被选中的页面: │ │ 1. MiUnlinkPageFromList() — 从工作集列表移除 │ │ 2. MmDeleteVirtualMapping() — 解除虚拟映射 │ │ 3. rmap 操作 — 删除反向映射记录 │ │ 4. MmWriteToSwapPage() — 将脏页写入交换文件 │ │ 5. MiInsertStandbyListAtFront() — 放入备用列表 │ │ │ └─ 更新可用页面计数 │ ├─ MiZeroPageThread(后台零页线程) │ → 从备用列表取页 → 置零 → 放入零页列表 │ └─ 等待内存压力事件或定时唤醒
2.5 池分配器流程
ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag) │ ├─ MmDeterminePoolType() — 确定池类型 │ → 非分页池 / 分页池 / 会话池 / 特殊池 │ ├─ 若为特殊池 → MmAllocateSpecialPool() │ → 分配对齐系统 PTE │ → 在分配区域前后设置填充模式 │ → 返回地址并记录元数据 │ ├─ 若为普通池: │ │ │ ├─ 检查小/中/大页面分配 │ │ → 小(< ~4KB):从池描述符(POOL_DESCRIPTOR)列表分配 │ │ → 中:从位图管理的池页分配 │ │ → 大:调用 MiAllocatePoolPages() 分配整页 │ │ │ ├─ MiAllocatePoolPages() │ │ → 调 MmRequestPageMemoryConsumer() │ │ → 底层调 MiRemoveAnyPage() 或 MiRemoveZeroPage() │ │ → 若失败 → 触发 MiTrimMemoryConsumer() 回收 │ │ → 若仍失败 → 触发 MmRebalanceMemoryConsumers() │ │ │ ├─ 写入池头部(Pool Header),包含标签、大小、前/后填充模式 │ │ │ └─ 返回分配地址(+ Pool Header 大小偏移) │ └─ ExFreePoolWithTag(Ptr) ├─ 检查是否为特殊池 → MmFreeSpecialPool() ├─ 检查填充模式是否被覆盖(越界检测) ├─ MiFreePoolPages() — 释放底层物理页 └─ 更新配额和统计
3. 数据流图
3.1 全局数据结构关系
┌─────────────────────────────────────────────────────────────┐ │ EPROCESS(进程对象) │ │ │ │ VadRoot (AVL 树根) ←────────────────── VAD 节点链表 │ │ ↓ ┌─────────────────┐ │ │ │ VAD 1: 0x00400000-0x00401000 │ StartingVpn │ │ │ │ VAD 2: 0x00402000-0x0040A000 │ EndingVpn │ │ │ │ VAD 3: ..... │ Protection │ │ │ │ │ ControlArea* ──┼──┐ │ │ │ └─────────────────┘ │ │ │ WorkingSetList(工作集页面链表) │ │ │ [PFN1, PFN15, PFN32, ...] │ │ │ │ │ │ DirectoryTableBase(页目录基址 PFN) │ │ └─────────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ CONTROL_AREA(控制区) │◄─┘ │ Segment(段对象指针) │ │ FilePointer(文件对象指针) ──→ 内存映射文件 │ │ TheaSegment(原型 PTE 数组) │ │ NumberOfSectionReferences / NumberOfMappedViews │ └─────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────┐ │ │ PFN DATABASE(页帧数据库) │ │ │ │ │ │ MmPfnDatabase[PhysicalPageNumber] │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ │ PFN Entry(~24 字节) │ │ │ │ │ ├─ PageFrameIndex(物理页号) │ │ │ │ │ ├─ PteAddress(指向映射此页的 PTE) ─────→ PTE │ │ │ │ │ ├─ ReferenceCount(引用计数) │ │ │ │ │ ├─ ShareCount(共享计数) │ │ │ │ │ ├─ Flags(页面状态:Modified/Dirty/Accessed/...) │ │ │ │ │ └─ ListEntry(指向所在列表:Free/Standby/Mod/Zero)│ │ │ │ └──────────────────────────────────────────────────┘ │ │ │ │ │ │ 页面列表链: │ │ │ FreeList[color] ←→ StandbyList[color] ←→ ModifiedList │ │ │ ↕ │ │ │ ZeroedList[color] │ │ └─────────────────────────────────────────────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ PTE(页表项 — 硬件页表) │ │ │ │ │ │ 有效 PTE: │ │ │ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬──────────────────────────────────┐ │ │ │ │V│R│U│W│C│A│D│L│G│N│ PageFrameNumber │ │ │ │ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴──────────────────────────────────┘ │ │ │ 交换 PTE: │ │ │ ┌─┬──────────────────────────────────────────────────────┐ │ │ │ │0│ PageFileNumber │ SwapSlotOffset │ │ │ │ └─┴──────────────────────────────────────────────────────┘ │ │ │ 原型 PTE: │ │ │ ┌─┬──────────────────────────────────────────────────────┐ │ │ │ │0│ ProtoPteBit │ PageFileNum │ SwapSlotOffset │ │ │ │ └─┴──────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────┘ │
3.2 关键数据流向
应用层 │ │ NtAllocateVirtualMemory / NtFreeVirtualMemory / NtProtectVirtualMemory │ NtCreateSection / NtMapViewOfSection ▼ ┌───────────────────────────────────────────────────────────┐ │ virtual.c — 系统调用处理层 │ │ (参数验证、VAD 操作、页表操作) │ └───────────────────┬───────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────┐ │ vadnode.c — VAD 树算法 │ │ (AVL 树插入/删除/查找/空闲区间搜索) │ └───────────────────┬───────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────┐ │ pagfault.c — 缺页异常处理 │ │ (DemandZero / PageFile / Transition / ProtoPte) │ └───┬───────────────┬───────────────┬───────────────────────┘ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────────┐ ┌──────────────────┐ │pfnlist.c │ │ pagefile.c │ │ rmap.c │ │PFN操作 │ │ 交换文件 I/O │ │ 反向映射管理 │ │页面链表 │ │ 槽位分配/释放│ │ 物理→虚拟查找 │ └────┬─────┘ └──────┬───────┘ └──────────────────┘ │ │ ▼ │ ┌──────────────┐ │ │ freelist.c │ │ │ 物理页统计 │ │ └──────────────┘ │ │ ▼ ┌───────────────────────────────────────────────────────────┐ │ balance.c — 内存平衡器 │ │ (回收策略、修剪消费者、工作集管理) │ └───────────────────────────────────────────────────────────┘ 池分配路径: ExAllocatePoolWithTag / ExFreePoolWithTag │ ▼ ┌───────────────────────────────────────────────────────────┐ │ expool.c — Executive 池分配器 │ │ (池描述符、标签跟踪、配额管理、大页表) │ └───────────────────┬───────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────┐ │ pool.c — 池物理页管理 │ │ (MiAllocatePoolPages / MiFreePoolPages / 非分页池初始化) │ └───────────────────┬───────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────┐ │ pfnlist.c — 底层物理页分配 │ │ (MiRemoveAnyPage / MiRemoveZeroPage / MiInsertPageInList) │ └───────────────────────────────────────────────────────────┘ Section 映射路径: NtCreateSection / NtMapViewOfSection │ ▼ ┌───────────────────────────────────────────────────────────┐ │ section.c — Section 对象管理 │ │ (控制区/段创建、视图映射、原型 PTE 管理、系统空间映射) │ └───┬───────────────┬───────────────────────────────────────┘ │ │ ▼ ▼ ┌──────────────┐ ┌──────────────┐ │ vadnode.c │ │ virtual.c │ │ VAD 插入 │ │ PTE 填充 │ └──────────────┘ └──────────────┘ 系统镜像加载路径: MmLoadSystemImage │ ▼ ┌───────────────────────────────────────────────────────────┐ │ sysldr.c — 系统加载器 │ │ (PE 解析、导入表解析、Thunk 修正、安全 Cookie、可分页性) │ └───┬───────────────┬───────────────────────────────────────┘ │ │ ▼ ▼ ┌──────────────┐ ┌──────────────────┐ │ section.c │ │ virtual.c │ │ 创建 Section │ │ 映射镜像视图 │ └──────────────┘ └──────────────────┘
3.3 物理页面生命周期状态机
┌──────────┐ ┌────────→│ Free │←────────┐ │ │ (空闲) │ │ │ └────┬─────┘ │ │ │ 分配 │ 释放 │ ▼ │ │ ┌──────────┐ │ │ │ Active │ │ │ │ (活跃) │ │ │ └────┬─────┘ │ │ │ 被换出 │ │ ▼ │ │ ┌──────────┐ │ │ │ Standby │ │ │ │ (备用) │ │ │ └────┬─────┘ │ │ │ 清零 │ │ ▼ │ │ ┌──────────┐ │ │ │ Zeroed │─────────┘ │ │ (已清零) │ │ └──────────┘ │ │ Active → Modified(页面被修改) │ Modified → Standby(修改写入交换文件后) │ Active → Transition(PFN 被其他进程共享但又暂时释放) │ Transition → Active(再次被引用时) │ └── 可用页面总数 = Free + Zeroed + Standby
4. 文件大小与关键统计
| 文件 | 估算行数 | 复杂度 |
|---|
ARM3/sysldr.c | ~3600 | ★★★★★ |
ARM3/section.c | ~3500 | ★★★★★ |
ARM3/virtual.c | ~3500 | ★★★★★ |
ARM3/expool.c | ~2500 | ★★★★ |
ARM3/pagfault.c | ~2000 | ★★★★ |
ARM3/mdlsup.c | ~2000 | ★★★★ |
ARM3/procsup.c | ~1800 | ★★★★ |
ARM3/pfnlist.c | ~1500 | ★★★ |
ARM3/vadnode.c | ~1200 | ★★★ |
ARM3/mminit.c | ~1200 | ★★★ |
| 其余文件 | <1000 各 | ★★ |
5. 关键设计决策
5.1 ARM3 vs 旧版 MM
- ARM3:对齐 Windows NT 架构,支持 x86-64、PAE、大页面、AWE 等特性。代码更结构化,复用 Windows 的命名和接口约定。
- 旧版 MM:ReactOS 原始实现,部分功能(
freelist.c、balance.c、pagefile.c)仍被 ARM3 调用。 - 过渡策略:ARM3 逐步覆盖旧版功能。目前 ARM3 提供虚拟内存核心(
virtual.c)、池分配器(expool.c)、缺页处理(pagfault.c)、Section(section.c)等主要功能,而旧版的marea.c、region.c、rmap.c等已有 ARM3 对应版本。
5.2 x86 页表自映射技巧
i386 架构下,ARM3 利用页目录的最后一项指向页目录自身,实现页表的虚拟可寻址性:
PD[1023] → PD 自身物理地址(虚拟地址 PTE_BASE 处可访问页表) PD[1022] → 超空间 PDE(用于临时映射物理页) PD[0..1019] → 进程页表项
这种设计使得内核可以通过PTE_BASE + (index * sizeof(PTE))直接访问任意 PTE,无需切换页目录。
5.3 PFN 数据库的物理页账本
每个物理页对应一个MMPFN条目(~24 字节),包含:
- 引用计数、共享计数
- PTE 指针(指向映射到此页的 PTE)
- 页面列表状态(Free/Standby/Modified/Zeroed/Bad/Transition)
- 颜色索引(Cache Color)
- 原始访问位和修改位备份
PFN 数据库本身位于非分页池中,由MmArmInitSystem在 Phase0 构建。
6. 与 Windows NT 的差异
| 特性 | Windows NT | ReactOS |
|---|
| 工作集管理器 | MmWorkingSetManager+KeBalanceSetManager | balance.c+wslist.cpp(简化实现) |
| 修改写入器 | MiModifiedPageWriter线程 | 依赖balance.c的回调机制 |
| 逻辑预取 | MmPrefetchPages完整实现 | mdlsup.c中有基本实现 |
| AWE | NtAllocateUserPhysicalPages完整 | ARM3 实现 |
| Session Pool | 完整支持 | session.c实现,功能有限 |
| ARM 架构 | 完整 | 大部分为 UNIMPLEMENTED 存根 |