ReactOS-Freeldr注册表HIVE文件格式2

上一节读了HIVE文件读入内存时的初始化操作。现在来看看实际对内存中的HIVE文件的操作。
首先是从空闲CELL中分配一个指定大小的CELL。HvAllocateCell就是做这件事情的。
这个函数有四个参数:
1. RegistryHive HHIVE结构指针
2. Size 需要分配的CELL大小(不包括HCELL结构的大小)
3. Storage 分配的CELL是Stable还是Volatile。
4. Vicinity 这个参数在Freeldr中没有使用

lib/cmlib/hivecell.c
  1. HCELL_INDEXCMAPIHvAllocateCell(PHHIVERegistryHive,SIZE_TSize,HSTORAGE_TYpestorage,HCELL_INDEXVicinity)
  2. {
  3. PHCELLFreeCell;
  4. HCELL_INDEXFreeCellOffset;
  5. PHCELLNewCell;
  6. PHBINBin;
  7. /*加上HCELL的大小并且按16位向上对齐*/
  8. Size=ROUND_UP(Size+sizeof(HCELL),16);
  9. /*从Freedisplay中找到Size大小的空闲CELL,FreeCellOffset是CELL的索引*/
  10. FreeCellOffset=HvpFindFree(RegistryHive,Size,Storage);
  11. /*如果没有符合的CELL,需要在末尾增加一个BIN,从新的BIN中分配需要的CELL,FreeCellOffset是CELL索引*/
  12. if(FreeCellOffset==HCELL_NIL)
  13. {
  14. Bin=HvpAddBin(RegistryHive,Storage);
  15. if(Bin==NULL)
  16. returnHCELL_NIL;
  17. FreeCellOffset=Bin->FileOffset+sizeof(HBIN);
  18. FreeCellOffset|=Storage<<HCELL_TYPE_SHIFT;
  19. }
  20. /*根据索引获得HCELL结构*/
  21. FreeCell=HvpGetCellHeader(RegistryHive,FreeCellOffset);
  22. /*分配的CELL有可能大于用户请求的长度,如果大于Size+16那么将CELL分解成两个。剩余的空间重新加入Freedisplay中*/
  23. if((ULONG)FreeCell->Size>Size+16)
  24. {
  25. NewCell=(PHCELL)((ULONG_PTR)FreeCell+Size);
  26. NewCell->Size=FreeCell->Size-Size;
  27. FreeCell->Size=Size;
  28. HvpAddFree(RegistryHive,NewCell,FreeCellOffset+Size);
  29. if(Storage==Stable)
  30. HvMarkCellDirty(RegistryHive,FreeCellOffset+Size,FALSE);
  31. }
  32. /*如果CELL类型是非易失的,需要将对应的DirtyVector置位,这样新增内容最终会被刷新到硬盘*/
  33. if(Storage==Stable)
  34. HvMarkCellDirty(RegistryHive,FreeCellOffset,FALSE);
  35. /*把这个CELL标记成已使用(大小为负)*/
  36. FreeCell->Size=-FreeCell->Size;
  37. RtlZeroMemory(FreeCell+1,Size-sizeof(HCELL));
  38. returnFreeCellOffset;
  39. }
这个函数从Freedisplay中搜索大小符合的CELL,如果不存在需要生成一个BIN。最后把CELL的索引返回给调用者。
因为调用者传入的Size不包含HCELL的大小,8行先调整大小并且向上按8字节对其。
10行调用HvpFindFree从Freedisplay中搜索符合Size大小的CELL,如果找到FreeCellOffset为CELL的索引。
如果没找则在末尾增加一个BIN,从这个BIN中分配符合大小的CELL,FreeCellOffset为CELL的索引(12-19)。
因为返回的CELL可能比请求的大,所以21行调用HvpGetCellHeader根据索引获得HCELL指针,如果返回的CELL比请求的大小大16字节以上的话则把CELL分配成两个,剩余的空间重新加入空闲列表Freedisplay(23-31)。
最后如果Storage为Stable时,说明这些信息最终要写入硬盘的HIVE文件,所以要把CELL所在Block的DirtyVetor置位,这样注册表刷新文件时会把这个改变回写到硬盘的HIVE文件中。(33-34)
因为我们分配的CELL项是空闲的,所以要把大小变为负数,代表此CELL已经被使用。最后把CELL内容清0,把CELL索引返回给调用者。

从Freedisplay中寻找空闲块是HvpFindFree的工作。
这个函数接受大小Size和存储类型Storage,返回找到的CELL的索引,如果没找到返回HCELL_NIL。
HvAllocateCell ->HvpFindFree
lib/cmlib/hivecell.c
  1. staticHCELL_INDEXCMAPIHvpFindFree(PHHIVERegistryHive,ULONGSize,HSTORAGE_TYpestorage)
  2. {
  3. PHCELL_INDEXFreeCellData;
  4. HCELL_INDEXFreeCellOffset;
  5. PHCELL_INDEXpFreeCellOffset;
  6. ULONGIndex;
  7. /*从Index开始递增搜索比Size大的CELL*/
  8. for(Index=HvpComputeFreeListIndex(Size);Index<24;Index++)
  9. {
  10. pFreeCellOffset=&RegistryHive->Storage[Storage].Freedisplay[Index];
  11. while(*pFreeCellOffset!=HCELL_NIL)
  12. {
  13. /*根据CELL的索引获得HCELL结构*/
  14. FreeCellData=(PHCELL_INDEX)HvGetCell(RegistryHive,*pFreeCellOffset);
  15. /*找到了比Size大的CELL,从空闲链里摘除,并返回给用户*/
  16. if((ULONG)HvpGetCellFullSize(RegistryHive,FreeCellData)>=Size)
  17. {
  18. FreeCellOffset=*pFreeCellOffset;
  19. *pFreeCellOffset=*FreeCellData;
  20. returnFreeCellOffset;
  21. }
  22. pFreeCellOffset=FreeCellData;
  23. }
  24. }
  25. returnHCELL_NIL;
  26. }
HvpFindFree会从小到大搜索Freedisplay链表,如果找到了大于等于Size的CELL,便把CELL从链表里面摘除,并把CELL的索引返回给用户。如果没有合适的空闲CELL将会返回HCELL_NIL。很直观。
这里有个有个HvGetCell函数,它的作用是根据CELL索引找到CELL指针(这个指针指向紧随着HCELL结构的内存地址)。我们来看看这个函数是如何实现的。
HvAllocateCell ->HvpFindFree ->HvpFindFree
lib/cmlib/hivecell.c
  1. PVOIDCMAPIHvGetCell(PHHIVERegistryHive,HCELL_INDEXCellIndex)
  2. {/*HvpGetCellHeader获得HCELL结构,紧跟着HCELL就是CELL内容,返回给用户*/
  3. return(PVOID)(HvpGetCellHeader(RegistryHive,CellIndex)+1);
  4. }
调用HvpGetCellHeader根据CELL索引获得HCELL结构,返回给用户。我们继续看HvpGetCellHeader。
HvAllocateCell ->HvpFindFree ->HvpFindFree -> HvpGetCellHeader
lib/cmlib/hivecell.c
  1. static__inlinePHCELLCMAPIHvpGetCellHeader(PHHIVERegistryHive,HCELL_INDEXCellIndex)
  2. {
  3. PVOIDBlock;
  4. if(!RegistryHive->Flat)
  5. {
  6. ULONGCellType;
  7. ULONGCellBlock;
  8. ULONGCellOffset;
  9. CellType=(CellIndex&HCELL_TYPE_MASK)>>HCELL_TYPE_SHIFT;//最高位是CellType(Stable或Volatile)
  10. CellBlock=(CellIndex&HCELL_BLOCK_MASK)>>HCELL_BLOCK_SHIFT;//Block是4kb,提取出Block的序号
  11. CellOffset=(CellIndex&HCELL_OFFSET_MASK)>>HCELL_OFFSET_SHIFT;//CELL在Block内的偏移
  12. //查询对应的BlockList表得到CELL地址
  13. Block=(PVOID)RegistryHive->Storage[CellType].BlockList[CellBlock].BlockAddress;
  14. return(PVOID)((ULONG_PTR)Block+CellOffset);
  15. }
  16. else
  17. {
  18. /*如果HIVE是以Flat模式打开的,将没有意义BlockList。同事BaseBlock指向的内容就是读出的HIVE文件本身。
  19. 这种模式只能读不能写。*/
  20. return(PVOID)((ULONG_PTR)RegistryHive->BaseBlock+HV_BLOCK_SIZE+
  21. CellIndex);
  22. }
  23. }
正常情况下Flat为FALSE,所以HvpGetCellHeader将把CellIndex分解为存储类型CellType、CELL所在块编号CellBlock、CELL在块内偏移CellOffset(6-11行)。
之后查询对应的BlockList表得到CELL的实际地址,返回给用户(13-14)。这个过程类似X86的页表,不过它只有一层 :)

这里有个特殊情况,就是HIVE可以以Flat模式初始化。这个模式下HIVE只读,并且没有初始化Storage数组,同事HHIVE.BaseBlock指针直接指向HIVE文件内容,20行处理了这种情况。

到这里我们了解了HvpFindFree、HvGetCell、HvpGetCellHeader三个函数的作用。
我们回到HvAllocateCell的第10行,这里调用HvpFindFree从Freedisplay中找到一个合适的空闲CELL,希望你还记得 :)
下面的代码你应该大部分经可以看明白了。还剩最后一点,如果HvpFindFree失败(没有合适的空闲区块),那么函数将会调用HvpAddBin在HIVE末尾新生成一个BIN,这个BIN将有足够的空间容纳请求的CELL。
这个函数逻辑很简单但代码比较多,大致是将请求的Size根据4kb向上对其,生成一个BIN,并且更新BlockList和DirtyVector,新生成的BIN占用的Block在尾部,具体代码这里就不列出了。有兴趣可以读读lib/cmlib/hivebin.c文件

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom