《Windows Internals》10.1.21 Cell maps:为什么在现代 Windows 里,cell index 已经不能再简单理解成“base + offset”,而要靠两级映射表去

张开发
2026/4/10 4:32:54 15 分钟阅读

分享文章

《Windows Internals》10.1.21 Cell maps:为什么在现代 Windows 里,cell index 已经不能再简单理解成“base + offset”,而要靠两级映射表去
个人主页杨利杰YJlio❄️个人专栏《Sysinternals实战教程》 《Windows PowerShell 实战》 《WINDOWS教程》 《IOS教程》《微信助手》 《锤子助手》 《Python》 《Kali Linux》《那些年未解决的Windows疑难杂症》让复杂的事情更简单让重复的工作自动化《Windows Internals》10.1.21 Cell maps为什么在现代 Windows 里cell index 已经不能再简单理解成“base offset”而要靠两级映射表去找真实内存位置《Windows Internals》10.1.21 Cell maps为什么在现代 Windows 里cell index 已经不能再简单理解成“base offset”而要靠两级映射表去找真实内存位置》1. 先说结论Cell maps 本质上就是“注册表版页表”2. 为什么早期可以直接 base offset后来却不行了2.1 为什么这种方法在启动早期成立2.2 但为什么后来就不行了3. 真正的问题出在哪出在“文件连续”不等于“内存连续”4. Cell maps 到底做了什么它把“cell index”翻译成“真实内存位置”4.1 为什么书里说它像页表5. 两级映射到底长什么样记住“1024 × 512”就够了6. 为什么偏偏是两级一级不行吗6.1 如果只做一级问题在哪6.2 两级的好处是什么7. 这里为什么还要特别提“一个 bin 内的 blocks 总在同一个 2 MB view 里”7.1 这意味着什么7.2 为什么这对理解 cell maps 很重要8. 为什么说 Winload 处理 SYSTEM hive 的方式恰好证明 cell maps 是“现代复杂度的产物”什么时候复杂度上升9. Cell maps 和前面的 Hive structure 是怎么接上的它其实是在回答“cell index 接下来怎么落地”10. 从桌面支持和系统学习视角这一节到底有什么价值10.1 它能帮我彻底放弃“hive 在内存里一定连续”的直觉10.2 它能帮我理解为什么 KCB、缓存、映射表这些东西都值得存在10.3 它能帮我理解“文件偏移”和“内存地址”不是同一回事10.4 它能帮我更自然地接受Configuration Manager 其实更像一个小型存储/映射子系统11. 最容易误解的 6 个点我帮你一次理顺11.1 误区一cell index 就是内存地址11.2 误区二只要知道 hive 基址永远都能 base offset11.3 误区三cell maps 只是一个简单数组11.4 误区四整个 hive 的内存布局完全随机11.5 误区五cell maps 是启动时就固定不变的11.6 误区六这只是底层实现细节对理解注册表没帮助12. 我的学习理解这一节真正教会我的是“注册表内部其实一直在做地址翻译”13. 总结提升下一篇预告《Windows Internals》10.1.21 Cell maps为什么在现代 Windows 里cell index 已经不能再简单理解成“base offset”而要靠两级映射表去找真实内存位置》读到《Windows Internals》10.1.21 Cell maps这一节时我最大的感受是前面Hive structure那一节刚把我从“注册表就是一棵树”拽到了block、bin、cell、cell index这一层而这一节又继续往下问了一个更硬核的问题既然 cell index 本质上只是 hive 文件里的偏移量那 Windows 在内存里到底是怎么把它翻译成真实地址的这就是cell maps要解决的核心问题。书里讲得非常直接如果 hive 永远不增长那么 Configuration Manager 完全可以把内存中的 hive 当作一个“连续文件映像”来看待直接用cell index hive 基址定位 cell系统启动早期Winload 处理 SYSTEM hive时确实就是这么干的。可问题在于hive 会不断增长系统会扩展 hive 文件、为新增 bin 分配新的 reserved views而这些视图在内存里不一定连续。一旦连续性被打破base offset这套朴素算法就不够用了于是 Configuration Manager 引入了一个类似页表的两级映射方案也就是cell maps。所以这篇文章我就专门把10.1.21 Cell maps讲透。这一节最核心的一句话就是cell index 从来都只是“hive 文件偏移”而不是天然可直接解引用的内存地址当 hive 的内存视图不再连续时Windows 就必须依靠 cell maps 这套两级映射结构把“文件偏移”翻译成“真实内存位置”。1. 先说结论Cell maps 本质上就是“注册表版页表”如果只用一句话总结这一节我会这样说Cell maps 的本质就是 Configuration Manager 为 hive 建立的一套“偏移量到内存地址”的翻译表。为什么我要直接把它叫做“注册表版页表”因为书里原文就用了一个非常关键的类比当 hive 在内存中的 reserved views 不再连续时Configuration Manager 采用的策略类似于 Windows 内存管理器把虚拟地址映射到物理地址的方式。也就是说cell index 虽然只是 hive 文件里的 offset但系统会通过一套two-level scheme去找出它最终落在哪个内存 block 上。这就意味着Cell maps 不是“附加小技巧”而是现代 Windows 在大 hive、动态增长、非连续映射前提下维持注册表可访问性的关键基础设施。2. 为什么早期可以直接base offset后来却不行了这一节最容易理解的切入点就是先把“旧办法为什么曾经可行”讲清楚。书里明确说如果 hive从来不增长Configuration Manager 完全可以把内存中的 hive 当作一个连续文件给定一个 cell index只要把这个偏移量加到内存中 hive 映像的基址上就能直接定位到对应 cell在系统启动早期Winload 处理 SYSTEM hive 时确实就是这么做的它把整个 SYSTEM hive 读进内存作为只读 hive然后通过base cell index定位 cells。2.1 为什么这种方法在启动早期成立因为那时候场景很“干净”SYSTEM hive 被整体读入视图是连续的还是只读使用也没有后续动态增长带来的视图扩展问题。所以这时最朴素的算法就够了cell index加到 hive 内存基址直接得到 cell 地址这套思路在“整体连续映像”的前提下完全成立。2.2 但为什么后来就不行了因为 hive 会长大。书里紧接着就说得很清楚hive 会随着新增 key 和 value 而增长系统必须为新 bin 分配新的 reserved views同时 hive 文件也会被扩展而这些 reserved views 在内存里aren’t necessarily contiguous。也就是说一旦 hive 变成“分散映射”的状态原来那种基址 偏移 地址的算法就失效了。因为 cell index 记录的是“文件里的相对偏移”不是“当前内存布局下的线性偏移”。3. 真正的问题出在哪出在“文件连续”不等于“内存连续”这一点是整节的灵魂。很多人第一次看到 cell index会自然地觉得既然它是 hive 文件里的 offset那系统把 hive 读进来以后偏移量照着加不就行了吗问题就在于现代 Windows 并不是永远把一个 growable hive 映射成单一连续内存块。书里已经明确点出了症结新增 bin 时系统会扩展 hive 文件同时为这些数据创建新的 reserved views但这些 views 在内存里未必连续。这就好比文件在磁盘逻辑上是一段连续地址空间但它被映射进内存时却可能被拆到多个分散区域于是cell index这类“文件偏移量”就不能直接拿来当内存地址算式的一部分了。我会把这个矛盾总结成一句话Cell index 描述的是“文件坐标”而程序真正需要的是“内存坐标”当文件和内存不再一一线性对应时就必须有一层翻译。4. Cell maps 到底做了什么它把“cell index”翻译成“真实内存位置”书里对 cell maps 的定义虽然没有单独一句“这是一个什么东西”但它给出的功能描述已经足够明确The scheme takes as input a cell index and returns as output both the address in memory of the block the cell index resides in and the address in memory of the block the cell resides in.换成更容易理解的话就是cell maps 的任务就是把“hive 文件偏移量”翻译成“某个内存块的位置”再进一步算出 cell 的准确地址。这就是一个标准的地址翻译模型。4.1 为什么书里说它像页表因为思路真的很像输入不是直接可用地址先拆字段再查目录再查下一级表最后得到块地址再加上页内/块内偏移定位最终目标。所以如果你熟悉内存管理那这一节其实会很顺cell index像“虚拟地址里的某种偏移表达”cell map directory / table像页目录和页表最后一个字段像页内偏移Windows 在这里并不是简单重复内存管理而是借用了“分级映射”这个非常成熟的思想去解决 hive 的非连续内存布局问题。5. 两级映射到底长什么样记住“1024 × 512”就够了这一节里最值得硬记的一个数字组合就是1024 × 512书里明确写到Configuration Manager 会把 cell index 拆成多个字段第一个字段作为hive 的 cell map directory的索引这个目录有1024 个 entry每个目录项指向一个cell map table每个 table 里有512 个 map entry第二个字段就是去索引这个 table最后一个字段再作为 block 内偏移精确定位 cell。这套结构我建议直接这样记cell index字段1: 目录索引cell map directory 1024项字段2: 表索引cell map table 512项定位到内存中的 block字段3: block 内偏移最终 cell 地址这张图的核心意思非常直白Cell maps 不是“算一下就完”而是“先查目录再查表再算偏移”。6. 为什么偏偏是两级一级不行吗书里虽然没有直接用一句“为什么是两级”的问答方式写出来但从上下文其实能推得很清楚。6.1 如果只做一级问题在哪如果想把所有 block 的映射都塞进一个巨大表里表会很大管理和扩展不够灵活hive 动态增长时不好按需扩展6.2 两级的好处是什么两级方案的好处就在于目录层固定表层按需创建hive 变大时可以动态增删相关表项不需要每次都重建一整张超大表。书里甚至明确说了hive 初始化时Configuration Manager 会动态创建这些 mapping tables随着 hive 大小变化还会在 cell directory 中增加或删除 tables。这正是两级映射最适合的场景既要支持大空间又要支持按需增长还要避免一次性摊开过大的映射成本。7. 这里为什么还要特别提“一个 bin 内的 blocks 总在同一个 2 MB view 里”这句话很容易被看过去但其实很关键。书里明确说一个 bin 可以包含一个或多个 blockhive 是按 bin 增长的因此 Windows 总是用一段连续内存来表示一个 bin所以一个 bin 内部的所有 block都会落在同一个2 MB hive mapped view里。7.1 这意味着什么意味着虽然整个 hive 的 views 可能是分散的但bin 本身仍然保持局部连续性。这是一种非常聪明的折中设计全局可以不连续局部容器仍尽量连续这样既能适应 hive 增长又不会把每个 bin 自己再切得过碎。7.2 为什么这对理解 cell maps 很重要因为 cell maps 最终要做的是先找到 cell 属于哪个 block而 block 又隶属于某个 binbin 在某个 2 MB view 里有连续映射。也就是说cell maps 并不是在一堆完全散乱的内存碎片里盲找而是建立在“bin 内局部连续、view 级分散”的前提上。这让 cell maps 更像一套“针对半连续、半分散布局”的翻译系统而不是面对完全随机碎片的暴力搜索。8. 为什么说 Winload 处理 SYSTEM hive 的方式恰好证明 cell maps 是“现代复杂度的产物”这一点特别有启发意义。书里在 Cell maps 开头举的例子非常典型启动早期Winload 读取整个 SYSTEM hive把它放进内存作为只读 hive然后直接用cell index hive 映像基址定位 cells。这说明在 hive 连续、简单、只读、未扩展的前提下根本不需要 cell maps。也就是说cell maps 不是“注册表天生就必须有”的结构而是随着系统复杂度上升才变得必要的。什么时候复杂度上升hive 会动态增长bin 会追加reserved views 不连续访问不再局限于启动早期只读场景。所以我很喜欢把这件事总结成一句话Winload 的“base offset”代表的是注册表的简单时代而 cell maps 代表的是注册表进入动态增长和复杂映射时代之后的答案。9. Cell maps 和前面的 Hive structure 是怎么接上的它其实是在回答“cell index 接下来怎么落地”前一篇 10.1.20 我们已经讲过hive 内部的 key/value/list/security 都存在 cell 里它们之间靠cell index建立联系cell index 本质上是cell 在 hive 文件中的 offset - base block 大小。但前一篇讲到这里其实还只回答了“逻辑引用怎么建立”。10.1.21 则继续往下回答了另一个更实际的问题好就算我已经有了 cell index这个“文件偏移量”在当前内存布局里到底怎么找到真实地址答案就是不能再简单base offset必须通过cell maps的两级翻译。所以这两节其实是严丝合缝接起来的10.1.20解释的是cell index 是什么10.1.21解释的是cell index 在现代系统里怎么被解读如果说 cell index 是“引用形式”那 cell maps 就是“引用解析器”。10. 从桌面支持和系统学习视角这一节到底有什么价值很多人会觉得这节太底层。但我觉得它的价值非常大尤其是对真正想把 Windows Internals 学扎实的人。10.1 它能帮我彻底放弃“hive 在内存里一定连续”的直觉这是最重要的一点。如果你一直假设 hive 在内存中天然连续那你后面看registry processsection-backed registry2 MB viewslarge SOFTWARE hive就会总觉得哪里别扭。cell maps 恰好把这个“非连续映射”的现实讲透了。10.2 它能帮我理解为什么 KCB、缓存、映射表这些东西都值得存在因为 Windows 处理注册表从来不是“找到路径就完事”而是要在复杂映射下高效定位内部数据。cell maps 就是这套复杂性的一部分。10.3 它能帮我理解“文件偏移”和“内存地址”不是同一回事这其实是系统底层非常关键的抽象能力。注册表这里再一次把这个观念强化了。10.4 它能帮我更自然地接受Configuration Manager 其实更像一个小型存储/映射子系统越往后看你会越发现注册表不是“树状配置表”那么简单它内部已经非常像一个专门的存储引擎了。11. 最容易误解的 6 个点我帮你一次理顺11.1 误区一cell index 就是内存地址不对。cell index 本质是hive 文件偏移量表达不是当前内存地址。11.2 误区二只要知道 hive 基址永远都能base offset只在连续只读映像场景里成立例如启动早期 Winload 处理 SYSTEM hive。现代 growable hive 场景并不成立。11.3 误区三cell maps 只是一个简单数组不对。书里明确说它是two-level scheme而且有1024 项目录 512 项表的分层结构。11.4 误区四整个 hive 的内存布局完全随机也不对。虽然全局 view 不一定连续但一个 bin 总会用连续内存表示并且 blocks within a bin 落在同一个 2 MB view 内。11.5 误区五cell maps 是启动时就固定不变的不对。书里明确说Configuration Manager 会动态创建这些 mapping tables并随着 hive 尺寸变化增加或删除表。11.6 误区六这只是底层实现细节对理解注册表没帮助恰恰相反。如果不懂这一层你很难真正理解 hive 增长、registry process、section-backed registry、KCB 缓存这些后续内容为什么都必须存在。12. 我的学习理解这一节真正教会我的是“注册表内部其实一直在做地址翻译”我觉得10.1.21 Cell maps最有价值的地方不是让我多记一个数据结构而是让我彻底意识到注册表内部不是“按路径找到 key 就完事”它还要不断做地址翻译。以前如果只停留在 Regedit 层面很容易觉得找到路径找到 key找到值就完了但读到这里我会明白Configuration Manager 背后其实还要面对一个完全不同的问题这些 key/value 对应的 cell 在 hive 文件里哪儿这些 bin 当前映射到内存的哪儿当前视图是不是连续这个文件偏移量如何翻译成内存位置这就让注册表一下子从“逻辑树世界”进入了“地址空间世界”。换句话说Cell maps 这节真正打碎的是“注册表访问只是路径查找”的错觉实际上它同时还是一个地址翻译问题。13. 总结提升如果让我用一句话总结《Windows Internals》10.1.21 Cell maps我会这样说在现代 Windows 里cell index 已经不能再简单理解成“hive 基址 偏移量”就能直接得到地址因为 growable hive 会不断扩展新 bin 对应的 reserved views 在内存中并不保证连续因此Configuration Manager 引入了类似页表的两级 cell maps把“hive 文件偏移量”翻译成“真实内存 block 和最终 cell 地址”。这篇最值得记住的 8 个结论是如果 hive 永远不增长Configuration Manager 完全可以把内存中的 hive 当成连续文件映像直接用base offset定位 cell。启动早期 Winload 处理 SYSTEM hive 时确实就是这么做的。问题出在 hive 会增长系统会扩展 hive 文件并分配新的 reserved views而这些 views 在内存中不一定连续。为了解决“文件偏移”和“内存位置”脱钩的问题Configuration Manager 引入了类似内存管理页表的 two-level scheme。cell map directory 有 1024 个 entry每个 entry 指向一个含 512 个 map entry 的 cell map table。cell index 被拆成多个字段先查目录再查表最后再用 block 内偏移找到精确 cell。一个 bin 总是用连续内存区域表示因此 bin 内所有 block 都位于同一个 2 MB hive mapped view 中。Configuration Manager 会在 hive 初始化时动态创建这些映射表并随 hive 尺寸变化动态增删。我觉得这一节最值得沉淀成自己一句话的理解就是Cell index 只是“文件坐标”cell maps 才负责把它变成“内存坐标”现代注册表的复杂度恰恰就藏在这层翻译里。下一篇预告《Windows Internals》10.1.22 The registry namespace and operation为什么应用眼里的HKLM\...到了内核里会先变成\Registry\Machine\...再由 Object Manager 和 Configuration Manager 接力解析》这一篇可以继续把\Registrykey object typeWindows object namespaceObject ManagerConfiguration Manager 接管路径解析key object 和 KCB 的关系全部串起来。返回顶部

更多文章