mm学习笔记_05:虚拟内存核心与系统调用
mm学习笔记_05虚拟内存核心与系统调用基于 ReactOS ARM3 MM 源码ntoskrnl/mm/ARM3/virtual.c1. virtual.c 概述文件位置d:\reactos\ntoskrnl\mm\ARM3\virtual.c职责虚拟内存管理的核心实现以下关键系统调用NtAllocateVirtualMemory— 分配虚拟内存NtFreeVirtualMemory— 释放虚拟内存NtProtectVirtualMemory— 修改内存保护属性MmGetPhysicalAddress— 虚拟地址转物理地址MmCopyVirtualMemory— 跨进程内存拷贝2. NtAllocateVirtualMemory——完整路径文件virtual.c:44572.1 函数签名NTSTATUS NTAPINtAllocateVirtualMemory(IN HANDLE ProcessHandle,IN OUT PVOID*BaseAddress,IN ULONG_PTR ZeroBits,IN OUT PSIZE_T RegionSize,IN ULONG AllocationType,IN ULONG Protect);2.2 步骤一参数验证// 1. ZeroBits 检查不能超过最大值if(ZeroBitsMI_MAX_ZERO_BITS)returnSTATUS_INVALID_PARAMETER_3;// 2. AllocationType 合法性检查必须包含 MEM_COMMIT / MEM_RESERVE / MEM_RESETif(AllocationType~(MEM_COMMIT|MEM_RESERVE|MEM_RESET|...))returnSTATUS_INVALID_PARAMETER_5;if(!(AllocationType(MEM_COMMIT|MEM_RESERVE|MEM_RESET)))returnSTATUS_INVALID_PARAMETER_5;// 3. MEM_RESET 独占检查if((AllocationTypeMEM_RESET)(AllocationType!MEM_RESET))returnSTATUS_INVALID_PARAMETER_5;// 4. MEM_LARGE_PAGES 必须带 MEM_COMMITif((AllocationTypeMEM_LARGE_PAGES)!(AllocationTypeMEM_COMMIT))returnSTATUS_INVALID_PARAMETER_5;// 5. MEM_WRITE_WATCH 必须带 MEM_RESERVEif((AllocationTypeMEM_WRITE_WATCH)!(AllocationTypeMEM_RESERVE))returnSTATUS_INVALID_PARAMETER_5;// 6. MEM_PHYSICAL 必须带 MEM_RESERVE 且 Protect PAGE_READWRITEif((AllocationTypeMEM_PHYSICAL)...)...// 7. 保护属性验证ProtectionMaskMiMakeProtectionMask(Protect);if(ProtectionMaskMM_INVALID_PROTECTION)returnSTATUS_INVALID_PAGE_PROTECTION;// 8. 用户态参数捕获SEH 保护_SEH2_TRY{if(PreviousMode!KernelMode){ProbeForWritePointer(UBaseAddress);ProbeForWriteSize_t(URegionSize);}PBaseAddress*UBaseAddress;PRegionSize*URegionSize;}_SEH2_EXCEPT(...);// 9. 地址范围检查if(PBaseAddressMM_HIGHEST_VAD_ADDRESS)returnSTATUS_INVALID_PARAMETER_2;if(!PRegionSize)returnSTATUS_INVALID_PARAMETER_4;2.3 步骤二进程获取与附加// 如果是当前进程直接使用if(ProcessHandleNtCurrentProcess())ProcessCurrentProcess;else{// 否则以 PROCESS_VM_OPERATION 权限引用目标进程StatusObReferenceObjectByHandle(ProcessHandle,PROCESS_VM_OPERATION,PsProcessType,...);// 如果目标进程不是当前进程附加到目标进程地址空间if(CurrentProcess!Process){KeStackAttachProcess(Process-Pcb,ApcState);AttachedTRUE;}}2.4 步骤三MEM_RESERVE 主路径当未指定基址或指定 MEM_RESERVE 时1. 配额计费 PsChargeProcessNonPagedPoolQuota(sizeof(MMVAD_LONG)) 2. 分配 VADExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), SdaV) 3. 初始化 VAD - Vad-u.VadFlags.PrivateMemory 1 - Vad-u.VadFlags.Protection ProtectionMask - if (AllocationType MEM_COMMIT) Vad-u.VadFlags.MemCommit 1 4. 插入 VAD自动查找空闲地址 Status MiInsertVadEx(Vad, StartingAddress, PRegionSize, HighestAddress, MM_VIRTMEM_GRANULARITY, AllocationType) 5. 写回用户参数SEH 保护 *URegionSize PRegionSize; *UBaseAddress (PVOID)StartingAddress;2.5 步骤四MEM_COMMIT 主路径当指定已有地址即对已预留区进行提交1. 锁定地址空间MmLockAddressSpace(AddressSpace) 2. 检查进程是否将终止 3. 查找冲突/匹配 VAD Result MiCheckForConflictingNode(StartVpn, EndVpn, Process-VadRoot, ...) 4. 验证 VAD 类型不能是 VadAwe/VadDevicePhysicalMemory/VadLargePages 5. 验证地址范围在 VAD 内 6. 如果是非私有内存Section 映射 - 获取原型 PTE - 锁定 MmSectionCommitMutex - 遍历 PTE 写入模板 PTE - 更新 NumberOfCommittedPages 7. 如果是私有内存 - 更新 CommitCharge 和 Process-CommitCharge - MiLockProcessWorkingSetUnsafe - 遍历每个 PTE - MiMakePdeExistAndMakeValid确保页表存在 - 写入 DemandZero 格式的 PTE保护属性 软件 PTE 标记 - MiUnlockProcessWorkingSetUnsafe完整流程图NtAllocateVirtualMemory │ ├─ 参数验证 │ ├─ ZeroBits 范围检查 │ ├─ AllocationType 组合检查 │ ├─ Protect 合法性检查 │ ├─ 用户态参数 Probe │ └─ 地址范围检查 │ ├─ 进程获取与附加KeStackAttachProcess │ ├─ [MEM_RESERVE 分支] │ ├─ 配额计费 │ ├─ 分配 VADExAllocatePoolWithTag │ ├─ VAD 初始化 │ └─ MiInsertVadEx自动搜索 Avl 插入 │ ├─ [MEM_COMMIT 分支] │ ├─ MmLockAddressSpace │ ├─ 查找已有 VADMiCheckForConflictingNode │ ├─ 验证 VAD 类型 │ ├─ [Section 内存] → 写原型 PTE → 更新段提交计数 │ ├─ [私有内存] → 更新 CommitCharge │ │ └─ 锁定工作集 │ │ └─ 遍历 PTE确保 PDE 有效写 DemandZero PTE │ └─ 解锁地址空间 │ └─ 返回 BaseAddress STATUS_SUCCESS3. NtFreeVirtualMemory——完整路径3.1 MEM_RELEASE 分支完整 VAD 释放四种释放情况情况1释放整个 VADBaseAddress VAD.StartingVpn, Size VAD 全范围 ─ 直接删除 VAD释放所有物理页 情况2释放 VAD 头部部分BaseAddress VAD.StartingVpn, Size VAD 全范围 ─ 调整 VAD.StartingVpn 原起始地址 Size ─ 释放对应 PTE 情况3释放 VAD 尾部部分BaseAddress VAD.StartingVpn, 释放到结束 ─ 调整 VAD.EndingVpn BaseAddress - 1 ─ 释放对应 PTE 情况4释放 VAD 中间部分释放中间一块两边保留 ─ 需要创建新 VAD 保存剩余后半部分 ─ 原 VAD 缩小到前半部分释放前 ┌──────────────────────────────┐ │ 原始 VAD │ └──────────────────────────────┘ 情况1 情况2 ┌──────────────────┐ ┌─────┐ ┌──────────┐ │ 全部释放 → 删除 │ │释放 │ │ 保留 │ └──────────────────┘ └─────┘ └──────────┘ Base StartingVpn 调整 情况3 情况4 ┌──────────┐ ┌─────┐ ┌─────┐ ┌──────┐ ┌─────┐ │ 保留 │ │释放 │ │释放 │ │ 保留1│ │保留2│ └──────────┘ └─────┘ └─────┘ └──────┘ └─────┘ (需要新建 VAD)3.2 MEM_DECOMMIT 分支取消提交// 1. 锁定地址空间// 2. 查找 VAD确认地址范围有效// 3. 调用 MiDecommitPages 取消提交// - 遍历指定范围的 PTE// - 对每个有效 PTE → 释放物理页、更新 PFN// - 将 PTE 写为 decommit 状态// 4. 更新进程 CommitCharge4. NtProtectVirtualMemory 路径通过MiProtectVirtualMemory实现1. 验证参数保护属性合法性 2. 锁定地址空间 3. 查找覆盖整个范围的 VAD 4. 调用 MiCheckSecuredVad 检查安全 VAD 限制 5. 锁定工作集 6. 遍历范围内的每个 PTE a. 如果 PTE 有效 - 修改硬件 PTE 的保护位 - 刷新 TLBKeFlushCurrentTb b. 如果 PTE 无效 - 修改软件 PTE 的保护位 c. 写时复制COW处理 - 如果新保护为只读但原为可写清除脏位 7. 更新 VAD 的保护属性 8. 解锁5. MiDeletePte——底层 PTE 删除逻辑文件virtual.c:369功能删除单个 PTE释放其引用的物理页。VOID NTAPIMiDeletePte(IN PMMPTE PointerPte,IN PVOID VirtualAddress,IN PEPROCESS CurrentProcess,IN PMMPTE PrototypePte){TempPte*PointerPte;if(TempPte.u.Hard.Valid0){// 无效 PTE可能是 Transition 或 PageFile 状态if(TempPte.u.Soft.Transition){// Transition PTE → 从列表中拆除递减引用MiUnlinkPageFromList(Pfn1);MiDecrementReferenceCount(Pfn1,PageFrameIndex);}// 忽略已换出PageFile的 PTE}else{// 有效 PTEPageFrameIndexPFN_FROM_PTE(TempPte);Pfn1MiGetPfnEntry(PageFrameIndex);if(Pfn1-u3.e1.PrototypePte1){// 原型 PTE → 递减页表共享计数和 PFN 共享计数MiDecrementShareCount(PageTablePfn,...);MiDecrementShareCount(Pfn1,PageFrameIndex);}else{// 普通 PTE → 标记删除递减共享计数MI_SET_PFN_DELETED(Pfn1);MiDecrementShareCount(Pfn1,PageFrameIndex);}// 擦除 PTEMI_ERASE_PTE(PointerPte);}KeFlushCurrentTb();}6. MiDeleteVirtualAddresses——批量 PTE 删除文件virtual.c:530功能批量删除指定虚拟地址范围内的所有 PTE。VOID NTAPIMiDeleteVirtualAddresses(IN ULONG_PTR Va,IN ULONG_PTR EndingAddress,IN OPTIONAL PMMVAD Vad){while(VaEndingAddress){// 逐级检查 PXE → PPE → PDE → PTE 的有效性// 跳过无效的大页表区域AddressGap// 对每个有效 PTE 调用 MiDeletePte// 递减页表引用计数必要时删除 PDEVaPAGE_SIZE;}}关键特点支持多级页表x86 PAE / x86-64的地址间隙跳过对 Section VAD 的间隙处理通过MI_GET_PROTOTYPE_PTE_FOR_VPN跳到下一个原型 PTE批量删除过程中保持 PFN 锁定7. MiDecommitPages——页面取消提交功能将已提交的页面取消提交释放物理内存但保留 VAD 结构。1. 遍历指定地址范围的每个 PTE 2. 对每个 PTE a. 如果 PTE 有效Valid1 - 获取 PFN - 标记 PFN 已删除MI_SET_PFN_DELETED - 递减 ShareCount - 擦除 PTE b. 如果 PTE 是 Transition 状态 - 从列表中拆除 PFN - 递减引用计数 c. 如果 PTE 是 DemandZero 状态 - 直接擦除 PTE 3. 更新进程/系统提交计数 4. 刷新 TLB8. MmGetPhysicalAddress——虚拟地址转物理地址文件virtual.c:5681PHYSICAL_ADDRESS NTAPIMmGetPhysicalAddress(PVOID Address){// 1. 检查 PDE 是否有效对大页 SPI 还检查 PPE/PXEif(MiAddressToPde(Address)-u.Hard.Valid){// 2. 检查是否为大页LargePageTempPde*MiAddressToPde(Address);if(TempPde.u.Hard.LargePage){// 大页偏移 地址低 22 位4MB 大页PhysicalAddress.QuadPart(ULONG64)TempPde.u.Hard.PageFrameNumberPAGE_SHIFT;PhysicalAddress.QuadPart((ULONG_PTR)Address(PAGE_SIZE*PTE_PER_PAGE-1));returnPhysicalAddress;}// 3. 检查 PTE 是否有效TempPte*MiAddressToPte(Address);if(TempPte.u.Hard.Valid){// 物理地址 PFN 12 页内偏移PhysicalAddress.QuadPart(ULONG64)TempPte.u.Hard.PageFrameNumberPAGE_SHIFT;PhysicalAddress.QuadPart((ULONG_PTR)Address(PAGE_SIZE-1));returnPhysicalAddress;}}// 4. 无效地址返回 0PhysicalAddress.QuadPart0;returnPhysicalAddress;}9. MmCopyVirtualMemory——跨进程内存拷贝文件virtual.c:1269两种策略基于数据量选择9.1 大拷贝 512 字节MDL 映射方式MiDoMappedCopyBufferSize MI_POOL_COPY_BYTES (512) → MiDoMappedCopy 流程 1. 循环拷贝每次最大 14 页MI_MAPPED_COPY_PAGES * PAGE_SIZE 56KB 2. 附加到源进程 → ProbeForRead → MmProbeAndLockPages → MmMapLockedPagesSpecifyCache 3. 分离源进程 4. 附加到目标进程 → ProbeForWrite → RtlCopyMemory 5. 分离目标进程 6. MmUnmapLockedPages → MmUnlockPages 7. 继续下一批数据9.2 小拷贝≤ 512 字节池缓冲方式MiDoPoolCopyBufferSize MI_POOL_COPY_BYTES (512) → MiDoPoolCopy 流程 1. 如果 BufferSize 512使用栈上缓冲StackBuffer否则分配 NonPagedPool 2. 附加到源进程 → ProbeForRead → RtlCopyMemory(池, 源) 3. 分离源进程 4. 附加到目标进程 → ProbeForWrite → RtlCopyMemory(目标, 池) 5. 分离目标进程 6. 重复直到所有数据拷贝完毕入口函数NTSTATUS NTAPIMmCopyVirtualMemory(PEPROCESS SourceProcess,PVOID SourceAddress,PEPROCESS TargetProcess,PVOID TargetAddress,SIZE_T BufferSize,KPROCESSOR_MODE PreviousMode,PSIZE_T ReturnSize){if(!BufferSize)returnSTATUS_SUCCESS;// 获取目标进程的 rundown protectionif(!ExAcquireRundownProtection(Process-RundownProtect))returnSTATUS_PROCESS_IS_TERMINATING;if(BufferSizeMI_POOL_COPY_BYTES)StatusMiDoMappedCopy(SourceProcess,SourceAddress,...);elseStatusMiDoPoolCopy(SourceProcess,SourceAddress,...);ExReleaseRundownProtection(Process-RundownProtect);returnStatus;}附录virtual.c 函数一览函数行号功能MiCalculatePageCommitment40计算指定地址范围内的已提交页数MiMakeSystemAddressValid181确保系统页表地址有效触发缺页MiDeleteSystemPageableVm273删除可分页系统内存MiDeletePte367删除单个 PTEMiDeleteVirtualAddresses528批量删除 PTEMiDoMappedCopy793MDL 方式跨进程拷贝MiDoPoolCopy1018池缓冲方式跨进程拷贝MmCopyVirtualMemory1269跨进程内存拷贝入口MiProtectVirtualMemory22 (extern)修改内存保护属性MiGetPageProtection1359获取页面的保护属性NtAllocateVirtualMemory4457虚拟内存分配系统调用MmGetPhysicalAddress5681虚拟地址→物理地址转换

相关新闻

【Claude Code】使用政策拒绝 Claude Code unable to respond Usage Policy refusal 应对

【Claude Code】使用政策拒绝 Claude Code unable to respond Usage Policy refusal 应对

文章目录 一、问题描述 1.1 环境信息 1.2 报错现象 二、根因分析 2.1 错误链路追踪 2.2 可能原因列举 三、解决方案 方案一:/rewind 回退到触发轮次之前(推荐) 方案二:/clear 启动全新会话 方案三:非交互模式(-p)的特殊处理 四、验证与回归测试 五、总结与预防 5.1 核心…

2026/7/22 22:30:14 阅读更多 →
07.20.每日总结

07.20.每日总结

7月20日总结 Linux基础 考试相关 复习前面讲过的所有命令 linux考试:91分 错题与不熟练的题: 存放了linux系统引导和启动时候使用的一些文件和目录的目录是/boot useradd添加用户时哪个选项可以指定描述信息-c 去掉vi编辑器中上一步错误的操作&#xff1…

2026/7/22 22:30:14 阅读更多 →
达梦数据库搭建(windows+Linux,图形化和命令行)

达梦数据库搭建(windows+Linux,图形化和命令行)

Windows上单机环境搭建Cmd命令systeminfo查看电脑信息确保1G可用内存根据电脑cpu和操作系统下载对应的数据库,并解压点击.iso光盘镜像文件安装选择时区并确定DM 数据库配置助手 来完成数据库初始化创建数据库实例选择数据库模板Linux上单机环境搭建获取对应的安装包…

2026/7/22 22:30:14 阅读更多 →

最新新闻

2026美赛数学建模D题完整参考论文(含模型建立求解、代码等)

2026美赛数学建模D题完整参考论文(含模型建立求解、代码等)

2026年美赛数学建模竞赛D题 平衡竞技与盈利:职业体育团队动态决策管理模型 摘要 在职业体育产业市场化、职业化程度不断提升的背景下,职业体育团队面临着赛场竞技表现突破与财务盈利能力提升的双重核心诉求,二者既相互支撑又存在潜在博弈&…

2026/7/22 23:14:04 阅读更多 →
HarmonyOS应用开发实战:萌宠日记 - 快捷操作图标网格布局

HarmonyOS应用开发实战:萌宠日记 - 快捷操作图标网格布局

HarmonyOS应用开发实战:萌宠日记 - 快捷操作图标网格布局 前言 快捷操作栏 是首页中用户快速访问各功能模块的入口。在 萌宠日记 的首页中,我们使用 5 个快捷操作图标(日记、档案、健康、相册、更多),采用 Row 水平布…

2026/7/22 23:14:04 阅读更多 →
HarmonyOS应用开发实战:萌宠日记 - 参数化路由

HarmonyOS应用开发实战:萌宠日记 - 参数化路由

HarmonyOS应用开发实战:萌宠日记 - 参数化路由 前言 在 萌宠日记 中,参数化路由 是页面间数据传递的另一种重要方式。与 回调函数模式 不同,参数化路由通过 NavPathStack.pushPath 的 param 参数传递数据,目标页面在 NavDestinat…

2026/7/22 23:14:04 阅读更多 →
深入解析qsort与冒泡排序

深入解析qsort与冒泡排序

目录 一,qsort函数 二,模仿qsort函数,利用冒泡排序排序任意类型的数据 三,sizeof和strlen对比 四,指针例题 1,指针-整数 2,指针-指针 3,指针复杂计算案例 一,qsort…

2026/7/22 23:14:04 阅读更多 →
HarmonyOS应用开发实战:萌宠日记 - 自定义 TabBar 设计与图标渲染

HarmonyOS应用开发实战:萌宠日记 - 自定义 TabBar 设计与图标渲染

HarmonyOS应用开发实战:萌宠日记 - 自定义 TabBar 设计与图标渲染 前言 TabBar 是底部导航栏的 视觉呈现核心,它直接影响用户对应用的第一印象和操作体验。在 萌宠日记 中,我们使用 Builder 装饰器 自定义了 TabBar 的渲染方式,实…

2026/7/22 23:14:04 阅读更多 →
第四篇:《函数、指针与结构体:组织代码的核心工具》

第四篇:《函数、指针与结构体:组织代码的核心工具》

变量是代码的“名词”,函数是代码的“动词”。在 Go 语言中,函数是第一公民,支持多返回值、命名返回值和闭包。指针提供了对内存的间接访问能力,但 Go 的指针比 C 更安全——不支持指针算术。结构体(struct&#xff09…

2026/7/22 23:13:03 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻