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、内核栈、AWE |
session.c | ~1100 行 | Session 生命周期:创建/删除/附加/分离 |
wslist.cpp | ~800 行 | 工作集列表管理(唯一 C++ 文件) |
balance.c | ~460 行 | 内存平衡器:后台线程页面回收 |
2. procsup.c — 进程地址空间管理
位置:ntoskrnl/mm/ARM3/procsup.c
2.1 MmCreateProcessAddressSpace
NTSTATUS NTAPIMmCreateProcessAddressSpace(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 NTAPIMmDeleteProcessAddressSpace(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] = 02.3 MmCreatePeb / MmCreateTeb
NTSTATUS NTAPIMmCreatePeb(IN PEPROCESS Process,OUT PVOID*Peb)位置:procsup.c:518
NTSTATUS NTAPIMmCreateTeb(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 的关键差异:
| 特性 | PEB | TEB |
|---|---|---|
| 大小 | sizeof(PEB) | sizeof(TEB) |
| 位置 | MM_HIGHEST_VAD_ADDRESS之上的随机地址 | 最高 VAD 地址范围内 |
| 保护 | OneSecured+ReadOnly | OneSecured |
| 用途 | 进程信息(堆、模块、LDR) | 线程信息(异常处理、TLS) |
2.4 MmCreateKernelStack / MmDeleteKernelStack
NTSTATUS NTAPIMmCreateKernelStack(IN BOOLEAN GuiStack,IN ULONG_PTR StackSize,OUT PVOID*StackBase)位置:procsup.c:276
VOID NTAPIMmDeleteKernelStack(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 NTAPINtAllocateUserPhysicalPages(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 位图volatileLONG MiSessionLeaderExists;// Session 领导者存在标志LIST_ENTRY MiSessionWsList;// Session 工作集链表3.3 MmSessionCreate
NTSTATUS NTAPIMmSessionCreate(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 = 13.4 MmSessionDelete
NTSTATUS NTAPIMmSessionDelete(IN ULONG SessionId)位置:session.c:887
3.5 MmAttachSession / MmDetachSession
VOID NTAPIMmAttachSession(IN PEPROCESS Process,IN PVOID SessionObject)位置:session.c:917
VOID NTAPIMmDetachSession(IN PEPROCESS Process)位置:session.c:985
MmAttachSession: ├─ 增加引用计数 ├─ 附加进程到 Session 空间 ├─ 设置 Process->Session 指针 ├─ 插入 Session 进程链表 └─ 激活 Session 空间(切换 PDE) MmDetachSession: ├─ 减少引用计数 ├─ 从 Session 进程链表移除 ├─ 如果引用为 0 → MiDereferenceSessionFinal │ ├─ 设置 DeletePending 标志 │ ├─ 等待所有进程分离 │ └─ 释放资源 └─ 清除 Process->Session 指针3.6 Session ID 位图管理
voidNTAPIMiInitializeSessionIds(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):
typedefstruct_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 NTAPIMiInitializeWorkingSetList(PMMWSL WsList,ULONG_PTR WsListPage)初始化工作集列表:
- 映射工作集列表页面
- 设置 ListHead、LastEntry、FirstFree 等字段
- 初始化分配位图
4.3 MiInsertInWorkingSetList
ULONG_PTR NTAPIMiInsertInWorkingSetList(PMMWSL WsList,PMMWSLE Wsle)在 WSL 中插入一个新的工作集条目:
- 从空闲链表获取一个槽位
- 写入 WSLE 数据
- 更新链表指针
4.4 MiRemoveFromWorkingSetList
VOID NTAPIMiRemoveFromWorkingSetList(PMMWSL WsList,ULONG_PTR Index)从 WSL 中移除一个条目:
- 标记为无效
- 插入空闲链表
- 如果移除的是最后一个条目,缩小列表
staticvoidFreeWsleIndex(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 NTAPIMmWorkingSetManager(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];// 消费者数组staticULONG MiMinimumAvailablePages=256;// 最小可用页数staticULONG MiMinimumPagesPerRun=256;// 每轮最少回收页数staticKEVENT MiBalancerEvent;// 平衡器事件staticKEVENT MiBalancerDoneEvent;// 完成事件staticKTIMER MiBalancerTimer;// 定时器// 消费者类型#defineMC_USER0// 用户进程#defineMC_MAXIMUM15.2 内存消费者
typedefstruct_MM_MEMORY_CONSUMER{ULONG PagesUsed;// 已使用页数ULONG PagesTarget;// 目标页数NTSTATUS(*Trim)(ULONG Target,ULONG Priority,PULONG NrFreed);// 修剪回调ULONG MinimumPages;// 最小页数}MM_MEMORY_CONSUMER;初始化:
CODE_SEG("INIT")VOID NTAPIMmInitializeBalancer(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 用户内存修剪
NTSTATUSMmTrimUserMemory(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 NTAPIMmRebalanceMemoryConsumers(VOID){// 如果平衡器不在运行,触发它if(InterlockedCompareExchange(&PageOutThreadActive,1,0)==0){KeSetEvent(&MiBalancerEvent,IO_NO_INCREMENT,FALSE);}}同步版本:
VOID NTAPIMmRebalanceMemoryConsumersAndWait(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”]