智能硬件行业资讯,IoT、机器人、AR/VR、智能家居前沿

MM 学习笔记 09:进程会话与工作集

MM 学习笔记 09:进程会话与工作集 - 图片1

MM 学习笔记 09:进程会话与工作集 - 图片2

MM 学习笔记 09:进程会话与工作集 - 图片3

MM 学习笔记 09:进程会话与工作集 文件:ntoskrnl/mm/ARM3/procsup.c + ntoskrnl/mm/ARM3/session.c + ntoskrnl/mm/ARM3/wslist.cpp + ntoskrnl/mm/balance.c 日期:2026-07-19 1. 概述 这一组文件管理三个紧密相关的子系统: 文件行数职责procsup.c~1600 行进程地址空间管理:PEB/TEB、内核栈、AWEsession.c~1100 行Session 生命周期:创建/删除/附加/分离wslist.cpp~800 行工作集列表管理(唯一 C++ 文件)balance.c~460 行内存平衡器:后台线程页面回收 2. procsup.c — 进程地址空间管理 位置: ntoskrnl/mm/ARM3/procsup.c 2.1 MmCreateProcessAddressSpace NTSTATUS NTAPI MmCreateProcessAddressSpace(IN ULONG MinWs, IN PEPROCESS Process, OUT PULONG_PTR DirectoryTableBase) 位置: procsup.c:1161 创建新进程的地址空间,分配页目录、超空间和工作集页面: MmCreateProcessAddressSpace │ ├─ 1. 生成进程颜色(用于缓存优化) │ └─ Process->NextPageColor = RtlRandom(&MmProcessColorSeed) │ ├─ 2. 初始化超空间锁 │ ├─ 3. 分配关键页面(锁定 PFN 数据库) │ │ │ ├─ TableBaseIndex → 页目录(Page Directory) │ │ └─ 使用 MiRemoveZeroPageSafe 优先获取零页 │ │ │ ├─ HyperIndex → 超空间页(Hyperspace) │ │ └─ 用于临时映射物理页 │ │ │ └─ WsListIndex → 工作集列表页(Working Set List) │ └─ 存储 WSL(Working Set List) │ ├─ 4. 设置基地址 │ ├─ Process->WorkingSetPage = WsListIndex │ └─ DirectoryTableBase[0/1] 赋值 │ ├─ 5. 架构相关初始化 │ └─ MiArchCreateProcessAddressSpace(Process, DirectoryTableBase) │ └─ 6. 插入进程链表 └─ InsertTailList(&MmProcessList, &Process->MmProcessLinks) 2.2 MmDeleteProcessAddressSpace VOID NTAPI MmDeleteProcessAddressSpace(IN PEPROCESS Process) 位置: procsup.c:1357 释放进程地址空间: MmDeleteProcessAddressSpace │ ├─ 1. 从进程链表移除 │ └─ RemoveEntryList(&Process->MmProcessLinks) │ ├─ 2. 从工作集扩展链表移除 │ └─ RemoveEntryList(&Process->Vm.WorkingSetExpansionLinks) │ ├─ 3. 释放工作集页面 │ ├─ MiGetPfnEntry(Process->WorkingSetPage) │ ├─ MI_SET_PFN_DELETED │ └─ MiDecrementShareCount → 两次(PTE 和页面) │ ├─ 4. 释放超空间页面 │ └─ 同样标记 PFN_DELETED 并减少引用 │ ├─ 5. 释放页目录 │ └─ PageFrameIndex = Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT │ MI_SET_PFN_DELETED │ MiDecrementShareCount × 2(自映射 PDE) │ ├─ 6. 释放 Session 引用 │ └─ MiReleaseProcessReferenceToSessionDataPage │ └─ 7. 清零目录基地址 └─ Process->Pcb.DirectoryTableBase[0/1] = 0 2.3 MmCreatePeb / MmCreateTeb NTSTATUS NTAPI MmCreatePeb(IN PEPROCESS Process, OUT PVOID * Peb) 位置: procsup.c:518 NTSTATUS NTAPI MmCreateTeb(IN PEPROCESS Process, OUT PVOID * Teb) 位置: procsup.c:753 这两个函数类似,都通过 MiCreatePebOrTeb 实现: MiCreatePebOrTeb(Process, Size, &BaseAddress) │ ├─ 1. 分配配额(PsChargeProcessNonPagedPoolQuota) │ ├─ 2. 分配 VAD(MMVAD_LONG) │ └─ 标签 'ldaV' │ ├─ 3. 设置 VAD 属性 │ ├─ CommitCharge = BYTES_TO_PAGES(Size) │ ├─ MemCommit = TRUE │ ├─ PrivateMemory = TRUE │ ├─ Protection = MM_READWRITE │ ├─ NoChange = TRUE │ └─ VadFlags2.OneSecured = TRUE (防误删) │ ├─ 4. 计算基地址 │ ├─ PEB: 随机选择 MM_HIGHEST_VAD_ADDRESS 之上的一个页面 │ ├─ TEB: 在用户栈附近 │ └─ 使用 MiInsertVadEx 插入 VAD 树 │ └─ 5. 提交页面 └─ MmCommitPageMemory → 分配物理页 PEB vs TEB 的关键差异: 特性PEBTEB大小sizeof(PEB)sizeof(TEB)位置MM_HIGHEST_VAD_ADDRESS 之上的随机地址最高 VAD 地址范围内保护OneSecured + ReadOnlyOneSecured用途进程信息(堆、模块、LDR)线程信息(异常处理、TLS)2.4 MmCreateKernelStack / MmDeleteKernelStack NTSTATUS NTAPI MmCreateKernelStack(IN BOOLEAN GuiStack, IN ULONG_PTR StackSize, OUT PVOID *StackBase) 位置: procsup.c:276 VOID NTAPI MmDeleteKernelStack(IN PVOID StackBase, IN BOOLEAN GuiStack) 位置: procsup.c:187 内核栈管理: 每个线程有一个内核栈GUI 线程(Win32k)需要更大的栈栈页面标记为 NoAccess 的守卫页用于检测栈溢出 MmCreateKernelStack: ├─ 分配连续虚拟地址空间(通过 MiAllocateVad) ├─ 提交物理页面(部分提交) ├─ 设置栈顶的守卫页(MM_GUARDPAGE) └─ 返回栈基地址 2.5 AWE 系统调用 AWE(Address Windowing Extensions)允许 32 位应用程序访问超过 4GB 的物理内存: NTSTATUS NTAPI NtAllocateUserPhysicalPages(IN HANDLE ProcessHandle, IN OUT PULONG_PTR NumberOfPages, OUT PULONG_PTR UserPfnArray) 位置: procsup.c:1434 当前实现是存根(stub)状态,尚未完全实现: NtAllocateUserPhysicalPages → DPRINT1("UNIMPLEMENTED\n") → return STATUS_NOT_IMPLEMENTED NtMapUserPhysicalPages → DPRINT1("UNIMPLEMENTED\n") → return STATUS_NOT_IMPLEMENTED NtAllocateUserPhysicalPagesEx → 同状态 3. session.c — Session 管理 位置: ntoskrnl/mm/ARM3/session.c Session 是 Windows 终端服务/多会话支持的核心概念。每个 Session 包含一组进程、一个独立的窗口站和桌面。 3.1 Session 空间地址范围 在 x86 32 位系统中: Session 空间: 0xA0000000 - 0xC0000000 (512MB) ↓ 页表覆盖整个 Session 区域 3.2 核心全局变量 PMM_SESSION_SPACE MmSessionSpace; // 当前 Session 空间指针 PFN_NUMBER MiSessionDataPages; // Session 数据页数 PFN_NUMBER MiSessionTagPages; // Session 标签页数 PFN_NUMBER MiSessionCreateCharge; // 创建 Session 的配额 KGUARDED_MUTEX MiSessionIdMutex; // Session ID 位图锁 PRTL_BITMAP MiSessionIdBitmap; // Session ID 位图 volatile LONG MiSessionLeaderExists; // Session 领导者存在标志 LIST_ENTRY MiSessionWsList; // Session 工作集链表 3.3 MmSessionCreate NTSTATUS NTAPI MmSessionCreate(OUT PULONG SessionId) 位置: session.c:827 MmSessionCreate │ ├─ 1. 检查进程是否已在 Session 中 │ ├─ 2. 设置 Session Leader │ └─ 原子操作 InterlockedCompareExchange(&MiSessionLeaderExists, 1, 0) │ ├─ 3. 调用 MiSessionCreateInternal 创建 Session │ │ │ ├─ 设置 PSF_SESSION_CREATION_UNDERWAY_BIT 标志 │ ├─ 分配页表(x86: 512MB 空间的页表) │ ├─ 从位图分配 Session ID │ ├─ 保留系统 PTE │ ├─ 分配数据页(零页) │ ├─ 分配标签页 │ ├─ 设置 PDE 映射 │ └─ 初始化 Session 空间 │ ├─ 4. 初始化 Session 工作集 │ └─ MiSessionInitializeWorkingSetList() │ └─ 5. 设置完成标志 └─ MmSessionSpace->u.Flags.Initialized = 1 3.4 MmSessionDelete NTSTATUS NTAPI MmSessionDelete(IN ULONG SessionId) 位置: session.c:887 3.5 MmAttachSession / MmDetachSession VOID NTAPI MmAttachSession(IN PEPROCESS Process, IN PVOID SessionObject) 位置: session.c:917 VOID NTAPI MmDetachSession(IN PEPROCESS Process) 位置: session.c:985 MmAttachSession: ├─ 增加引用计数 ├─ 附加进程到 Session 空间 ├─ 设置 Process->Session 指针 ├─ 插入 Session 进程链表 └─ 激活 Session 空间(切换 PDE) MmDetachSession: ├─ 减少引用计数 ├─ 从 Session 进程链表移除 ├─ 如果引用为 0 → MiDereferenceSessionFinal │ ├─ 设置 DeletePending 标志 │ ├─ 等待所有进程分离 │ └─ 释放资源 └─ 清除 Process->Session 指针 3.6 Session ID 位图管理 void NTAPI MiInitializeSessionIds(VOID) { // 计算需要的数据页数 MiSessionDataPages = ROUND_TO_PAGES(sizeof(MM_SESSION_SPACE)) >> PAGE_SHIFT; // 标签页大小(池标签跟踪) MiSessionTagSizePages = 2; MiSessionBigPoolPages = 1; MiSessionTagPages = MiSessionTagSizePages + MiSessionBigPoolPages; // 创建 Session 的总配额 MiSessionCreateCharge = 1 + MiSessionDataPages + MiSessionTagPages; // 初始化位图(初始 64 个 Session ID) MiSessionIdBitmap = ExAllocatePoolWithTag(PagedPool, sizeof(RTL_BITMAP) + BitmapSize, TAG_MM); RtlInitializeBitMap(MiSessionIdBitmap, (PVOID)(MiSessionIdBitmap + 1), Size); RtlClearAllBits(MiSessionIdBitmap); } 3.7 Session 生命周期图 ┌────────────┐ │ 系统启动 │ └─────┬──────┘ │ ▼ ┌──────────────────┐ │ MiInitialize │ │ SessionIds │ ← 初始化 ID 位图 └──────┬───────────┘ │ ▼ ┌──────────────────┐ │ MmSessionCreate │ ← 创建 Session │ │ │ │ ├─ 分配 Session │ │ │ ID 和数据页 │ │ ├─ 设置 PDE │ │ └─ 初始化工作集 │ └──────┬───────────┘ │ ▼ ┌──────────────────┐ │ MmAttachSession │ ← 进程附加到 Session │ └─ 增加引用计数 │ └──────┬───────────┘ │ ▼ ┌──────────────────┐ │ MmDetachSession │ ← 进程分离 │ └─ 减少引用计数 │ └──────┬───────────┘ │ ▼ (引用计数 = 0) ┌──────────────────────┐ │ MiDereference │ ← 最终清理 │ SessionFinal │ │ ├─ 设置 Delete │ │ │ Pending │ │ ├─ 等待分离完成 │ │ └─ 释放所有资源 │ └──────────────────────┘ 4. wslist.cpp — 工作集列表管理 位置: ntoskrnl/mm/ARM3/wslist.cpp 注意:这是 MM 子系统中唯一的 C++ 文件,使用了 C++ 特性(引用传递等)。 4.1 核心数据结构 PMMWSL MmWorkingSetList; // 系统工作集列表 PMMWSL MmSystemCacheWorkingSetList; // 系统缓存工作集列表 KEVENT MmWorkingSetManagerEvent; // 工作集管理器事件 工作集列表条目(WSLE): typedef struct _MMWSLE { union { struct { ULONG_PTR Valid : 1; // 有效位 ULONG_PTR Locked : 1; // 锁定位 ULONG_PTR Cache : 1; // 缓存位 ULONG_PTR Protection : 5; // 保护 ULONG_PTR VirtualPageIndex : 56; // 虚拟页索引 } e1; struct { ULONG_PTR NextFree : 28; // 下一个空闲条目 ULONG_PTR PreviousFree : 28; // 上一个空闲条目 ULONG_PTR MustBeZero : 8; } Free; LONGLONG Long; } u1; } MMWSLE, *PMMWSLE; 4.2 MiInitializeWorkingSetList NTSTATUS NTAPI MiInitializeWorkingSetList(PMMWSL WsList, ULONG_PTR WsListPage) 初始化工作集列表: 映射工作集列表页面设置 ListHead、LastEntry、FirstFree 等字段初始化分配位图 4.3 MiInsertInWorkingSetList ULONG_PTR NTAPI MiInsertInWorkingSetList(PMMWSL WsList, PMMWSLE Wsle) 在 WSL 中插入一个新的工作集条目: 从空闲链表获取一个槽位写入 WSLE 数据更新链表指针 4.4 MiRemoveFromWorkingSetList VOID NTAPI MiRemoveFromWorkingSetList(PMMWSL WsList, ULONG_PTR Index) 从 WSL 中移除一个条目: 标记为无效插入空闲链表如果移除的是最后一个条目,缩小列表 static void FreeWsleIndex(PMMWSL WsList, ULONG Index) { PMMWSLE Wsle = WsList->Wsle; ULONG& LastEntry = WsList->LastEntry; ULONG& FirstFree = WsList->FirstFree; ULONG& LastInitializedWsle = WsList->LastInitializedWsle; Wsle[Index].u1.Long = 0; // 擦除条目 if (Index == (LastEntry - 1)) { // 释放最后一个条目,缩小列表 while (Wsle[Index].u1.e1.Valid == 0) Index--; LastEntry = Index + 1; // 收缩已分配的内存 while ((LastInitializedWsle - LastEntry) > (PAGE_SIZE / sizeof(MMWSLE))) { // 释放一页 PFN_NUMBER Page = PFN_FROM_PTE(PointerPte); ntoskrnl::MiPfnLockGuard PfnLock; PMMPFN Pfn = MiGetPfnEntry(Page); MI_SET_PFN_DELETED(Pfn); MiDecrementShareCount(...); } } } 4.5 MmWorkingSetManager 工作集管理器 NTSTATUS NTAPI MmWorkingSetManager(ULONG_PTR Parameter) 工作集管理器负责在内存压力下修剪工作集: MmWorkingSetManager │ ├─ 等待事件(MmWorkingSetManagerEvent) │ ├─ 遍历所有进程的工作集 │ ├─ 计算需要修剪的页面数 │ ├─ 修剪(Trim) │ └─ 从 WSL 中移除页面,放回备用列表 │ └─ 重复 4.6 TrimWsList 工作集修剪 修剪算法的核心:遍历工作集列表,将最近未访问的页面换出。 TrimWsList(WsList, Target) │ ├─ 遍历 WSLE 条目 │ ├─ 检查 Accessed 位 │ ├─ 已访问 → 清除 Accessed 位,跳过 │ └─ 未访问 → 准备换出 │ ├─ 锁定页面(防止正在使用) │ ├─ 为页面分配页文件槽位 │ ├─ 写入页面内容到交换文件 │ ├─ 从工作集移除 │ └─ MiRemoveFromWorkingSetList │ └─ 更新统计 5. balance.c — 内存平衡器 位置: ntoskrnl/mm/balance.c 5.1 核心数据结构 MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM]; // 消费者数组 static ULONG MiMinimumAvailablePages = 256; // 最小可用页数 static ULONG MiMinimumPagesPerRun = 256; // 每轮最少回收页数 static KEVENT MiBalancerEvent; // 平衡器事件 static KEVENT MiBalancerDoneEvent; // 完成事件 static KTIMER MiBalancerTimer; // 定时器 // 消费者类型 #define MC_USER 0 // 用户进程 #define MC_MAXIMUM 1 5.2 内存消费者 typedef struct _MM_MEMORY_CONSUMER { ULONG PagesUsed; // 已使用页数 ULONG PagesTarget; // 目标页数 NTSTATUS (*Trim)(ULONG Target, ULONG Priority, PULONG NrFreed); // 修剪回调 ULONG MinimumPages; // 最小页数 } MM_MEMORY_CONSUMER; 初始化: CODE_SEG("INIT") VOID NTAPI MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages) { memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers)); MiMinimumAvailablePages = 256; MiMinimumPagesPerRun = 256; MiMemoryConsumers[MC_USER].PagesTarget = NrAvailablePages / 2; } 5.3 MiBalancerThread 平衡器线程主循环 MiBalancerThread(PVOID Unused) { PVOID WaitObjects[2]; WaitObjects[0] = &MiBalancerEvent; // 手动触发 WaitObjects[1] = &MiBalancerTimer; // 定时触发 while (TRUE) { KeSetEvent(&MiBalancerDoneEvent, IO_NO_INCREMENT, FALSE); // 等待事件或定时器 KeWaitForMultipleObjects(2, WaitObjects, ...); // 计算需要回收的页面数 Target = 256; // 遍历消费者 MiTrimMemoryConsumer(MC_USER, Target); } } 5.4 MmTrimUserMemory 用户内存修剪 NTSTATUS MmTrimUserMemory(ULONG Target, ULONG Priority, PULONG NrFreedPages) 两种模式: Priority = 0:仅清除 Accessed 位(不实际换出)Priority = 1:实际换出页面(调用 MmPageOutPhysicalAddress) MmTrimUserMemory │ ├─ Priority = 0 (低优先级) │ └─ 遍历用户页面的反向映射 │ └─ Pte->u.Hard.Accessed = 0 (清除访问位) │ └─ Priority = 1 (高优先级) └─ 调用 MmPageOutPhysicalAddress 换出页面 ├─ 获取反向映射条目 ├─ 锁定地址空间 ├─ 写入交换文件 └─ 释放物理页面 5.5 MmRebalanceMemoryConsumers 重新平衡 VOID NTAPI MmRebalanceMemoryConsumers(VOID) { // 如果平衡器不在运行,触发它 if (InterlockedCompareExchange(&PageOutThreadActive, 1, 0) == 0) { KeSetEvent(&MiBalancerEvent, IO_NO_INCREMENT, FALSE); } } 同步版本: VOID NTAPI MmRebalanceMemoryConsumersAndWait(VOID) { KeResetEvent(&MiBalancerDoneEvent); MmRebalanceMemoryConsumers(); KeWaitForSingleObject(&MiBalancerDoneEvent, ...); } 5.6 平衡器流程图 ┌──────────────────────────┐ │ 系统初始化 │ │ MmInitializeBalancer │ │ └─ 设置目标页数 │ └──────────┬───────────────┘ │ ▼ ┌──────────────────────────┐ │ 创建平衡器线程 │ │ PsCreateSystemThread │ │ → MiBalancerThread │ └──────────┬───────────────┘ │ ▼ ┌──────────┐ │ 等待事件 │ ←──────┐ │ (定时/触发)│ │ └─────┬────┘ │ │ │ ▼ │ ┌──────────┐ │ │ 计算目标 │ │ │ Target=256│ │ └─────┬────┘ │ │ │ ▼ │ ┌──────────┐ │ │ MiTrim │ │ │ Consumer │ │ └─────┬────┘ │ │ │ ▼ │ ┌──────────┐ │ │ 设置完成 │─────────┘ │ 事件 │ └──────────┘ 6. 三个子系统的关系 ┌─────────────────────────────────────────────────────┐ │ balance.c │ │ MiBalancerThread │ │ │ │ │ MmRebalanceMemoryConsumers │ │ │ │ └────────────────────┼─────────────────────────────────┘ │ 触发 ▼ ┌─────────────────────────────────────────────────────┐ │ wslist.cpp │ │ MmWorkingSetManager │ │ │ │ │ Trim → MiRemoveFromWorkingSetList │ │ │ │ └────────────────────┼─────────────────────────────────┘ │ 页面回收 ▼ ┌─────────────────────────────────────────────────────┐ │ procsup.c │ │ MmCreateProcessAddressSpace │ │ MmCreatePeb / MmCreateTeb │ │ MmCreateKernelStack │ │ │ │ │ ▼ │ │ MmDeleteProcessAddressSpace │ │ │ │ └────────────────────┼─────────────────────────────────┘ │ Session 管理 ▼ ┌─────────────────────────────────────────────────────┐ │ session.c │ │ MmSessionCreate → MmAttachSession │ │ MmSessionDelete → MmDetachSession │ │ MiSessionCreateInternal │ └─────────────────────────────────────────────────────┘ 关键交互路径: 进程创建 → MmCreateProcessAddressSpace → 分配页目录/工作集页Session 创建 → MmSessionCreate → MiSessionCreateInternal → 分配 Session 空间内存压力 → MmRebalanceMemoryConsumers → MiBalancerThread → Trim → 页面回收工作集修剪 → MmWorkingSetManager → TrimWsList → 页面换出 参考链接 [procsup.c — 进程地址空间管理](file:///d:/reactos/ntoskrnl/mm/ARM3/procsup.c)[session.c — Session 管理](file:///d:/reactos/ntoskrnl/mm/ARM3/session.c)[wslist.cpp — 工作集列表](file:///d:/reactos/ntoskrnl/mm/ARM3/wslist.cpp)[balance.c — 内存平衡器](file:///d:/reactos/ntoskrnl/mm/balance.c)[Windows Internals 7th Part 1, Chapter 10 “Memory Management”]