发布时间:2026/7/24 23:15:05
MM 学习笔记 07:池分配器 (Pool Allocator) MM 学习笔记 07池分配器 (Pool Allocator)文件ntoskrnl/mm/ARM3/expool.cntoskrnl/mm/ARM3/pool.c日期2026-07-191. 概述池分配器是内核态的堆管理器提供分页池PagedPool和非分页池NonPagedPool两类内存分配。两个核心文件分工如下文件行数职责expool.c~3100 行Executive 池分配器 —ExAllocatePoolWithTag/ExFreePoolWithTag、标签跟踪系统、热标签缓存、大页表管理、配额定级pool.c~1200 行底层页面管理 —MiAllocatePoolPages/MiFreePoolPages、非分页池初始化、已释放池页面保护分配层次结构ExAllocatePoolWithTag (expool.c) │ ▼ POOL_DESCRIPTOR 空闲列表查找 (expool.c) │ ▼ MiAllocatePoolPages (pool.c) ← 底层页面分配 │ ▼ MiRemoveAnyPage / MiRemoveZeroPage (pfnlist.c) ← PFN 数据库2. POOL_DESCRIPTOR 结构typedefstruct_POOL_DESCRIPTOR{POOL_TYPE PoolType;// 池类型分页/非分页ULONG PoolIndex;// 池索引ULONG RunningAllocs;// 累计分配计数ULONG RunningDeAllocs;// 累计释放计数ULONG TotalPages;// 总页数ULONG TotalBigPages;// 大页面数ULONG Threshold;// 阈值决定何时切换到大页面分配PVOID LockAddress;// 锁地址PVOID PendingFrees;// 待释放链表LONG PendingFreeDepth;// 待释放深度SIZE_T TotalBytes;// 总字节数SIZE_T Spare0;LIST_ENTRY ListHeads[POOL_LISTS_PER_PAGE];// 空闲列表头256个}POOL_DESCRIPTOR,*PPOOL_DESCRIPTOR;// miarm.h 中的定义#definePOOL_LISTS_PER_PAGE(PAGE_SIZE/sizeof(LIST_ENTRY))// 32位: 4096/8 512 个桶 (但只用 256 个)// 64位: 4096/16 256 个桶关键理解ListHeads包含256 个空闲列表头每个对应一个固定大小的块范围桶索引计算NumberOfBytes POOL_BLOCK_SHIFTPOOL_BLOCK_SHIFT 4即 16 字节粒度当请求大小 PoolDesc-Threshold时走大页分配路径3. POOL_HEADER — 池头部每个分配块前有一个 8 字节头部用于链表管理和完整性检查typedefstruct_POOL_HEADER{union{struct{#ifdef_WIN64USHORT PreviousSize:8;// 前一个块的大小POOL_BLOCK_SIZE 为单位USHORT PoolIndex:8;// 池索引USHORT BlockSize:8;// 当前块的大小USHORT PoolType:8;// 池类型#elseUSHORT PreviousSize:9;USHORT PoolIndex:7;USHORT BlockSize:9;USHORT PoolType:7;#endifULONG PoolTag;// 4字节标签};ULONGLONG Reserved;};}POOL_HEADER,*PPOOL_HEADER;布局示意32位PreviousSize:9 | PoolIndex:7 | BlockSize:9 | PoolType:7PoolTag:32相关宏#definePOOL_ENTRY(x)(PPOOL_HEADER)((ULONG_PTR)(x)-sizeof(POOL_HEADER))#definePOOL_BLOCK(x,i)(PPOOL_HEADER)((ULONG_PTR)(x)((i)*POOL_BLOCK_SIZE))#definePOOL_NEXT_BLOCK(x)POOL_BLOCK((x),(x)-BlockSize)#definePOOL_PREV_BLOCK(x)POOL_BLOCK((x),-((x)-PreviousSize))4. 全局变量// expool.c - 核心全局变量POOL_DESCRIPTOR NonPagedPoolDescriptor;// 非分页池描述符全局唯一PPOOL_DESCRIPTOR ExpPagedPoolDescriptor[161];// 分页池描述符每CPU或每节点PPOOL_DESCRIPTOR PoolVector[2];// [0]分页池, [1]非分页池KSPIN_LOCK ExpTaggedPoolLock;// 标签跟踪表锁KSPIN_LOCK ExpLargePoolTableLock;// 大页表锁PPOOL_TRACKER_TABLE PoolTrackTable;// 池标签跟踪哈希表PPOOL_TRACKER_BIG_PAGES PoolBigPageTable;// 大页跟踪表ULONG PoolHitTag;// 热标签命中统计ULONG ExpPoolFlags;// 调试标志特殊池、验证器等// pool.c - 池页面管理全局变量LIST_ENTRY MmNonPagedPoolFreeListHead[MI_MAX_FREE_PAGE_LISTS];// 4个空闲页链表PFN_COUNT MmNumberOfFreeNonPagedPool;// 空闲非分页池页数SLIST_HEADER MiNonPagedPoolSListHead;// 非分页池 SListLookasideSLIST_HEADER MiPagedPoolSListHead;// 分页池 SListBOOLEAN MmProtectFreedNonPagedPool;// 释放池页面保护开关5. ExAllocatePoolWithTag 完整分配路径PVOID NTAPIExAllocatePoolWithTag(IN POOL_TYPE PoolType,IN SIZE_T NumberOfBytes,IN ULONG Tag)函数位置expool.c:19045.1 分配流程图ExAllocatePoolWithTag │ ├─ 1. 检查特殊池/验证器标志 │ └─ ExpPoolFlags POOL_FLAG_SPECIAL_POOL → MmAllocateSpecialPool │ ├─ 2. 获取池描述符 │ └─ PoolDesc PoolVector[PoolType BASE_POOL_TYPE_MASK] │ ├─ 3. 检查 Lookaside 缓存快速路径 │ └─ 如果命中直接返回无需遍历空闲列表 │ ├─ 4. 计算桶索引 │ └─ i NumberOfBytes POOL_BLOCK_SHIFT (4) │ ├─ 5. 遍历空闲列表从 i 开始向上搜索 │ │ │ ├─ 找到足够大的块 → 从链表中移除 │ │ │ ├─ 碎片分割Split │ │ └─ 如果剩余大小 ≥ 最小块大小16字节 │ │ 将剩余部分作为自由块插回链表 │ │ │ └─ 未找到 → 调用 MiAllocatePoolPages 获取新页面 │ ├─ 6. 大页分配路径 │ └─ NumberOfBytes POOL_MAX_ALLOC (约 PAGE_SIZE/8) │ → MiAllocatePoolPages(OriginalType, NumberOfBytes) │ └─ 7. 更新标签跟踪表 └─ ExpInsertPoolTrackerTag → 记录到哈希表5.2 Lookaside 缓存优化Lookaside 是每个 CPU 的快速分配缓存使用 SList单向链表// 初始化InitializeSListHead(MiPagedPoolSListHead);InitializeSListHead(MiNonPagedPoolSListHead);// 默认最大值MiNonPagedPoolSListMaximum4;// 非分页池最多缓存4个条目MiPagedPoolSListMaximum8;// 分页池最多缓存8个条目// 在内存较大的系统上可以扩大if(MmNumberOfPhysicalPages(2*_1GB)/PAGE_SIZE){MiNonPagedPoolSListMaximum*8;MiPagedPoolSListMaximum*8;}5.3 碎片分割示例假设分配 48 字节3 × 16从桶索引 3 开始搜索找到的块大小为 80 字节5 × 16[分配前] |←──── 80 字节 (5 块) ────→| [分配后] |← 48 →|←── 32 (剩余) ──→| ↑分配返回 ↑插回空闲列表6. ExFreePoolWithTag 释放路径VOID NTAPIExFreePoolWithTag(IN PVOID P,IN ULONG TagToFree)函数位置expool.c:24946.1 释放流程图ExFreePoolWithTag │ ├─ 1. 检查特殊池 │ └─ MmIsSpecialPoolAddress → MmFreeSpecialPool │ ├─ 2. 检查大页释放 │ └─ PAGE_ALIGN(P) P → 大页释放路径 │ ├─ ExpFindAndRemoveTagBigPages 查找标签和页数 │ └─ MiFreePoolPages 释放页面 │ ├─ 3. 小/中块释放正常路径 │ │ │ ├─ 获取 POOL_HEADER验证标签和完整性 │ │ │ ├─ 尝试前向合并Forward Combine │ │ └─ 检查下一个块是否是空闲块 │ │ 如果是合并两个块 │ │ │ ├─ 尝试后向合并Backward Combine │ │ └─ 检查上一个块是否是空闲块 │ │ 如果是合并两个块 │ │ │ └─ 将合并后的块插入空闲列表 │ ├─ 4. 尝试释放整页 │ └─ 如果合并后的块占满整个页面 │ 从空闲列表移除 → MiFreePoolPages │ └─ 5. 更新标签跟踪表 └─ ExpRemovePoolTrackerTag6.2 合并示例// 合并前假设块 B 刚被释放[块 A:已分配][块 B:刚刚释放][块 C:已分配]↓// 尝试前向合并检查块 C 是否空闲 → 否// 尝试后向合并检查块 A 是否空闲 → 否// 结果块 B 插入空闲列表// 合并前块 A 和块 B 都是空闲[块 A:空闲][块 B:刚刚释放][块 C:已分配]↓// 前向合并检查块 C → 否// 后向合并检查块 A → 是空闲// 合并后[块 AB: 更大的空闲块] [块 C: 已分配]7. 标签跟踪系统池标签跟踪是一个哈希表用于跟踪每个标签的分配统计信息typedefstruct_POOL_TRACKER_TABLE{ULONG Key;// 标签4字节ASCIIULONG NonPagedAllocs;// 非分页分配次数ULONG NonPagedFrees;// 非分页释放次数SIZE_T NonPagedBytes;// 非分页使用字节数ULONG PagedAllocs;// 分页分配次数ULONG PagedFrees;// 分页释放次数SIZE_T PagedBytes;// 分页使用字节数}POOL_TRACKER_TABLE,*PPOOL_TRACKER_TABLE;大页跟踪表类似typedefstruct_POOL_TRACKER_BIG_PAGES{PVOID Va;// 虚拟地址ULONG Key;// 标签ULONG Pages;// 页数}POOL_TRACKER_BIG_PAGES,*PPOOL_TRACKER_BIG_PAGES;查找逻辑// 计算哈希Hash(Tag4)PoolTrackTableMask;// 线性探测while(PoolTrackTable[Hash].Key!Tag){if(HashPoolTrackTableMask)Hash0;}8. 热标签缓存ExpSeedHotTagsExpSeedHotTags在池系统初始化时预填充跟踪表将常用标签预先固定到哈希表的特定位置提高查找效率。VOID NTAPIExpSeedHotTags(VOID){ULONG i,Key,Hash,Index;PPOOL_TRACKER_TABLE TrackTablePoolTrackTable;ULONG TagList[]{ oI, laH,PldM,LooP,tSbO, prI,bdDN,LprI,pOoI, ldM,eliF,aVMC,dSeS,CFtN,looP,rPCT,bNMC,dTeS,sFtN,TPCT,CPCT, yeK,qSbO,mNoI,aEoI,cPCT,aFtN,0ftN,tceS,SprI,ekoT, eS,lCbO,cScC,lFtN,cAeS,mfSF,kWcC,miSF,CdfA,EdfA,orSF,nftN,PRIU,rFpN,RFpN,aPeS,sUeS,FpcA,MpcA,cSeS,mNbO,sFpN,uLeS,DPcS,nevE,vrqR,ldaV, pP,SdaV, daV,LdaV,FdaV, GIB,};// 遍历所有 64 个常用标签for(i0;i64;i){KeyTagList[i];Hash(Key4)PoolTrackTableMask;// 在线性探测过程中插入...}}这些标签涵盖内核中最频繁分配的标签如ldaVVAD、 oI、 laH等。9. MiAllocatePoolPages 底层页面分配// pool.c - 从非分页池预分配页面PVOID NTAPIMiAllocatePoolPages(IN POOL_TYPE PoolType,IN SIZE_T NumberOfBytes)位置pool.c功能检查是否是分页池调用 → 转发到分页池分配器非分页池从MmNonPagedPoolFreeListHead空闲列表中获取页面支持 4 个空闲列表减少多核竞争按颜色分配分配成功后将新页面分割成适当的块插入 POOL_DESCRIPTOR 的空闲列表更新配额统计// 非分页池空闲列表4个减少锁定争用for(i0;iMI_MAX_FREE_PAGE_LISTS;i){InitializeListHead(MmNonPagedPoolFreeListHead[i]);}10. MiFreePoolPages 底层页面释放将整页池内存归还给系统从 POOL_DESCRIPTOR 空闲列表中移除该页上的所有空闲块将页面插入MmNonPagedPoolFreeListHead空闲列表如果启用了MmProtectFreedNonPagedPool对页面进行保护更新MmNumberOfFreeNonPagedPool计数11. 非分页池初始化流程CODE_SEG(INIT)VOID NTAPIMiInitializeNonPagedPool(VOID)位置pool.c:278初始化步骤MiInitializeNonPagedPool │ ├─ 1. 初始化 SListLookaside 缓存 │ └─ 根据物理内存大小调整最大缓存数 │ ├─ 2. 初始化 4 个空闲列表头 │ ├─ 3. 计算初始池大小 │ └─ PoolPages BYTES_TO_PAGES(MmSizeOfNonPagedPoolInBytes) │ ├─ 4. 创建第一个空闲条目 │ └─ FreeEntry-Size PoolPages │ FreeEntry-Owner FirstEntry │ ├─ 5. 插入到空闲列表 │ └─ InsertHeadList(MmNonPagedPoolFreeListHead[MI_MAX_FREE_PAGE_LISTS-1], ...) │ └─ 6. 初始化阈值和事件 └─ MiInitializeNonPagedPoolThresholds() └─ MiInitializePoolEvents()阈值初始化// 低阈值8MB 或非分页池大小的 1/3MiLowNonPagedPoolThreshold(8*_1MB)PAGE_SHIFT;MiLowNonPagedPoolThresholdmin(MiLowNonPagedPoolThreshold,Size/3);// 高阈值20MB 或非分页池大小的 50%MiHighNonPagedPoolThreshold(20*_1MB)PAGE_SHIFT;MiHighNonPagedPoolThresholdmin(MiHighNonPagedPoolThreshold,Size/2);12. 已释放池页面保护机制当MmProtectFreedNonPagedPool TRUE时启用用于检测 Use-After-Free 错误。VOID NTAPIMiProtectFreeNonPagedPool(IN PVOID VirtualAddress,IN ULONG PageCount){PMMPTE PointerPte,LastPte;MMPTE TempPte;// 遍历页面的所有 PTEPointerPteMiAddressToPte(VirtualAddress);LastPtePointerPtePageCount;do{TempPte*PointerPte;// 标记为无效 PTE设置原型位以识别为池页面TempPte.u.Hard.Valid0;TempPte.u.Soft.Prototype1;MI_WRITE_INVALID_PTE(PointerPte,TempPte);}while(PointerPteLastPte);// 刷新 TLBKeFlushEntireTb(TRUE,TRUE);}对应的解除保护函数MiUnProtectFreeNonPagedPool在需要访问被保护的页面时调用。保护下的链表操作// 插入前临时解除保护MiProtectedPoolUnProtectLinks(ListHead,PoolFlink,PoolBlink);// 执行链表操作InsertHeadList(ListHead,Entry);// 重新保护MiProtectedPoolProtectLinks(PoolFlink,PoolBlink);13. 状态图ExAllocatePoolWithTag │ ▼ ┌─────────────────┐ │ 特殊池检查 │───启用──→ MmAllocateSpecialPool └────────┬────────┘ │ 禁用 ▼ ┌─────────────────┐ │ Lookaside 缓存 │───命中──→ 直接返回 └────────┬────────┘ │ 未命中 ▼ ┌─────────────────┐ │ 计算桶索引 │ └────────┬────────┘ ▼ ┌─────────────────┐ │ 遍历空闲列表 │ └────────┬────────┘ │ │ 找到块 未找到 │ │ ▼ ▼ ┌─────────┐ ┌──────────────┐ │ 碎片分割 │ │ MiAllocate │ │ │ │ PoolPages │ └────┬─────┘ └──────┬───────┘ │ │ ▼ ▼ ┌──────────────────────────┐ │ 更新标签跟踪表 │ │ ExpInsertPoolTrackerTag│ └──────────────────────────┘参考链接[miarm.h 中 POOL_HEADER 和 POOL_DESCRIPTOR 定义](file:///d:/reactos/ntoskrnl/mm/ARM3/miarm.h)[expool.c — Executive 池分配器主文件](file:///d:/reactos/ntoskrnl/mm/ARM3/expool.c)[pool.c — 池页面管理](file:///d:/reactos/ntoskrnl/mm/ARM3/pool.c)[special.c — 特殊池和 Driver Verifier](file:///d:/reactos/ntoskrnl/mm/ARM3/special.c)[Windows Internals 7th Part 1, Chapter 10 “Memory Management” — Pool]

相关新闻

2026/7/24 23:15:05

基于HarmonyOS的AI春联对联生成——从对齐到评估的全流程技术实践

基于HarmonyOS的AI春联对联生成——从对齐到评估的全流程技术实践 一、项目背景与需求分析(Align) 1.1 场景痛点分析 在现代数字生活中,用户对春联对联生成的需求日益增长。传统的春联对联生成方式存在效率低下、个性化不足等问题。通过AI技术…

2026/7/24 23:15:05

Linear Loops功能详解:简化循环工程操作与自动化工作流

Linear 最近发布了 Loops 功能,这是一个专门为简化循环工程操作而设计的新工具。如果你经常需要处理重复性任务、批量操作或者需要自动化的工作流程,Loops 可能会成为你的新利器。这次我们重点看看它的核心功能、使用门槛、实际效果以及如何快速上手。Lo…

2026/7/25 0:40:46

小爱音箱变身AI管家:5分钟打造你的智能语音助手终极指南

小爱音箱变身AI管家:5分钟打造你的智能语音助手终极指南 【免费下载链接】mi-gpt 🏠 将小爱音箱接入 ChatGPT 和豆包,改造成你的专属语音助手。 项目地址: https://gitcode.com/GitHub_Trending/mi/mi-gpt 想让家里的小爱音箱不再只是…

2026/7/25 0:40:46

猫抓视频嗅探工具:你的网页视频下载专家指南

猫抓视频嗅探工具:你的网页视频下载专家指南 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 在数字内容无处不在的今天,我们…

2026/7/25 0:40:46

商家降本、骑手增收,诚心呈意为何是配送行业的破局答案?

跑同城配送的骑手,大多有过这样的感受:路上同行的人越来越多,手里的配送单价却一降再降,想多赚点就得熬更长的时间、跑更远的路;做餐饮的商家也有苦难言:线上订单看着红火,扣完平台抽成和配送费…

2026/7/25 0:40:46

Web 安全怎么学,OWASP Top 10 漏洞原理与 Burp Suite 实战详解

从原理到实战:Web 核心漏洞深度剖析 很多初学者在掌握了 Linux 基础和网络协议后,面对 Web 渗透测试往往感到无从下手。要么过度依赖自动化工具“一键扫描”,要么死记硬背 Payload 却不懂其背后的运行逻辑。真正的进阶之路,在于深…

2026/7/25 0:35:46

2026年商用清洁机器人品牌推荐榜:五大品牌全场景实力解析

商用清洁机器人正在从“买一台设备试试看”,进入按场景、面积和运营目标成套部署的新阶段。 国际机器人联合会(IFR)发布的《World Robotics 2025—Industrial Robots》显示,2024年全球工业机器人新安装量达到542076台&#xff0c…

2026/7/23 12:54:51

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/25 0:00:15

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:00:15

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:00:15

VHF 甚高频语音喊话系统(桥梁智能防撞场景)核心优势

一、直达船员,预警链路最短营运船舶强制标配 VHF 船载电台,属于驾驶室常态化值守设备;预警语音直接传递至驾驶人员,区别于岸上声光报警(船员经常听不到)、短信 / 小程序(船员极少主动查看&#…

2026/7/23 23:42:43

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…