ReactOS-Freeldr注册表HIVE文件格式

ReactOS的注册表信息存储在ReactOS/System32/CONfig/SYstem文件中。注册文件使用的一种特殊的格式——HIVE。
HIVE文件主要由BASE_BLOCK/BIN/CELL三部分组成的。BASE_BLOCK是文件头,大小为4KB,里面存储了整个文件的一些全局信息。BIN是以4KB对其的一段空间,里面管理了若干个CELL。CELL是一段用户自定义大小的空间,注册表的键值信息就是存储在一个个CELL里面。
整个HIVE文件就是一个BASE_BLOCK头和若干BIN,每个BIN中有若干CELL。当用户需要分配一个CELL,而HIVE文件中没有足够大的CELL时,需要在文件末尾增长一个BIN,再从这个BIN里面分别需要的CELL。

我们来看看整个HIVE文件在内存中的结构
  1. typedefstruct _HHIVE
  2. {
  3. ULONG Signature; // HV_SIGNATURE(0x66676572) 标志位
  4. PGET_CELL_ROUTINE GetCellRoutine; // 根据index获得cell的函数指针
  5. PRELEASE_CELL_ROUTINE ReleaseCellRoutine; // 释放cell指针
  6. PALLOCATE_ROUTINE Allocate; // 申请内存的指针
  7. PFREE_ROUTINE Free; // 释放内存的指针
  8. PFILE_READ_ROUTINE FileRead; // 读HIVE文件指针
  9. PFILE_WRITE_ROUTINE FileWrite; // 写HIVE文件
  10. PFILE_SET_SIZE_ROUTINE FileSetSize; // 获得HIVE文件大小
  11. PFILE_FLUSH_ROUTINE FileFlush; // 刷新HIVE
  12. PHBASE_BLOCK BaseBlock; // BASE_BLOCK的内存指针
  13. RTL_BITMAP DirtyVector; // 标志哪个块(4kb)被修改过的位图
  14. ULONG DirtyCount; // 被修改块的数量?
  15. ULONG DirtyAlloc; // ?
  16. ULONG BaseBlockAlloc; ULONG Cluster; // BaseBlock占用的Cluster数量,总为1
  17. BOOLEAN Flat; // 初始化HIVE时是否使用了HINIT_FLAT标志
  18. BOOLEAN ReadOnly; // 是否只读
  19. BOOLEAN Log; BOOLEAN DirtyFlag; ULONG HvBinHeadersUse;
  20. ULONG HvFreeCellsUse;
  21. ULONG HvUsedcellsUse;
  22. ULONG CmUsedCellsUse;
  23. ULONG HiveFlags; // Hive标志
  24. ULONG LogSize;
  25. ULONG RefreshCount;
  26. ULONG StorageTypeCount; // Storage数组的大小(2)
  27. ULONG Version;
  28. DUAL Storage[HTYPE_COUNT]; // 管理Block的数组,Stable和Volatile各一个元素
  29. } HHIVE,*PHHIVE;
在freeldr中整个HIVE文件使用HHIVE数据结构来表示。这里面有一组函数指针,当HIVE需要分配内存、写文件等操作时会调用相应的指针。这些指针在HIVE的初始化函数中被赋值。GetCellRoutine可以根据CELL索引获取CELL指针,ReleaseCellRoutine会释放CELL指针。这两个指针在freeldr中没有使用。
初始化后BaseBlock指针将指向HIVE的文件头BASE_BLOCK结构。
DirtyVector是一个位图,标志着HIVE文件中的哪个块(4KB)被修改过了。
Cluster是BaseBlock占用的簇的数量,在reactos系统中这个值永远为1.
Flat代表初始化时是否使用了HINIT_FLAT标志,使用这个标志说明HHIVE所指向的内存都是初始化时分配好的,所以不需要它释放内存。ReadOnly代表注册表只读。
最后一个比较重要的域是StorageTypeCount和Storage数组。

HIVE中的数据分为两大类,易失的(Volatile)和非易失去的(Stable),StorageTypeCount为2。Stable数据最后会被刷新到硬盘文件中,而Volatile数据关机之后就丢失了。HHIVE中Storage数组有两个元素,分别管理这两种数据的全部数据块,这里面存储了数据块位置,是否空闲等信息。之后我们会看到用户通过CELL索引获取CELL地址,这个转换就使用到了这个数组里面的信息。

下面我们看一个HIVE的初始化函数HvpInitializeMemoryHive
函数有两个参数:
Hive是HHIVE结构的指针
ChunkBase是从硬盘中读取的HIVE文件内容

lib/cmlib/hiveinit.c
  1. NTSTATUS CMAPI HvpInitializeMemoryHive(PHHIVE Hive, PVOID ChunkBase)
  2. {
  3. SIZE_T BlockIndex;
  4. PHBIN Bin,NewBin;
  5. ULONG i;
  6. ULONG BitmapSize;
  7. PULONG BitmapBuffer;
  8. SIZE_T ChunkSize;
  9. /* 得到HIVE文件大小,并且将HBASE_BLOCK.Length改为块大小4kb */
  10. ChunkSize =((PHBASE_BLOCK)ChunkBase)->Length;
  11. ((PHBASE_BLOCK)ChunkBase)->Length = HV_BLOCK_SIZE;
  12. /* 如果文件小于HBASE_BLOCK或者头部校验失败,数据不合法 */
  13. if(ChunkSize <sizeof(HBASE_BLOCK)||
  14. !HvpVerifyHiveHeader((PHBASE_BLOCK)ChunkBase))
  15. {
  16. return STATUS_REGISTRY_CORRUPT;
  17. }
  18. /* 生成Hive->BaseBlock中生成空间,将ChunkBase的头部复制进去 */
  19. Hive->BaseBlock = Hive->Allocate(sizeof(HBASE_BLOCK),FALSE,TAG_CM);
  20. if(Hive->BaseBlock ==NULL)
  21. {
  22. return STATUS_NO_MEMORY;
  23. }
  24. RtlcopyMemory(Hive->BaseBlock,ChunkBase,sizeof(HBASE_BLOCK));
  25. /* 除文件头的每个BLOCK(4kb)准备一个HMAP_ENTRY结构,存放在Hive.Storage[Stable]中 */
  26. Hive->Storage[Stable].Length =(ULONG)(ChunkSize / HV_BLOCK_SIZE)-1;
  27. Hive->Storage[Stable].BlockList =
  28. Hive->Allocate(Hive->Storage[Stable].Length *
  29. sizeof(HMAP_ENTRY),TAG_CM);
  30. if(Hive->Storage[Stable].BlockList ==NULL)
  31. {
  32. Hive->Free(Hive->BaseBlock,0);
  33. return STATUS_NO_MEMORY;
  34. }
  35. /* 每个BIN循环一次 这个循环初始化HMAP_ENTRY数组 */
  36. for(BlockIndex =0; BlockIndex < Hive->Storage[Stable].Length;)
  37. {
  38. /* 获得BIN结构 */
  39. Bin =(PHBIN)((ULONG_PTR)ChunkBase +(BlockIndex +1)* HV_BLOCK_SIZE);
  40. if(Bin->Signature != HV_BIN_SIGNATURE ||
  41. (Bin->Size % HV_BLOCK_SIZE)!=0)
  42. {
  43. Hive->Free(Hive->BaseBlock,0);
  44. Hive->Free(Hive->Storage[Stable].BlockList,0);
  45. return STATUS_REGISTRY_CORRUPT;
  46. }
  47. /* 赋值一份BIN到本地 */
  48. NewBin = Hive->Allocate(Bin->Size,TRUE,TAG_CM);
  49. if(NewBin ==NULL)
  50. {
  51. Hive->Free(Hive->BaseBlock,0);
  52. return STATUS_NO_MEMORY;
  53. }
  54. Hive->Storage[Stable].BlockList[BlockIndex].BinAddress =(ULONG_PTR)NewBin;
  55. Hive->Storage[Stable].BlockList[BlockIndex].BlockAddress =(ULONG_PTR)NewBin;
  56. RtlcopyMemory(NewBin,Bin,Bin->Size);
  57. /* 每个BIN管理若干block */
  58. if(Bin->Size > HV_BLOCK_SIZE)
  59. {
  60. for(i =1; i < Bin->Size / HV_BLOCK_SIZE; i++)
  61. {
  62. Hive->Storage[Stable].BlockList[BlockIndex + i].BinAddress =(ULONG_PTR)NewBin;
  63. Hive->Storage[Stable].BlockList[BlockIndex + i].BlockAddress =
  64. ((ULONG_PTR)NewBin +(i * HV_BLOCK_SIZE));
  65. }
  66. }
  67. /* 下一个BIN */
  68. BlockIndex += Bin->Size / HV_BLOCK_SIZE;
  69. }
  70. /* 初始化空闲CELL表 */
  71. if(HvpCreateHiveFreeCellList(Hive))
  72. {
  73. HvpFreeHiveBins(Hive);
  74. Hive->Free(Hive->BaseBlock,0); font-style:normal!important; text-decoration:none!important"> /* 初始化Hive->DirtyVector位图 */
  75. BitmapSize = ROUND_UP(Hive->Storage[Stable].Length,
  76. sizeof(ULONG)*8)/8;
  77. BitmapBuffer =(PULONG)Hive->Allocate(BitmapSize,TAG_CM);
  78. if(BitmapBuffer ==NULL)
  79. {
  80. HvpFreeHiveBins(Hive);
  81. Hive->Free(Hive->BaseBlock,0);
  82. return STATUS_NO_MEMORY;
  83. }
  84. RtlInitializeBitMap(&Hive->DirtyVector,BitmapBuffer,BitmapSize *8);
  85. RtlClearallBits(&Hive->DirtyVector);
  86. return STATUS_SUCCESS;
  87. }
HvpInitializeMemoryHive先生成HBASE_BLOCK所需空间赋值给HHIVE中的BaseBlock指针,并拷贝ChunkBase的头部到这段区域(9-24行)。
这里有个地方需要注意,在这里传入的HBASE_BLOCK.Length存储的是整个文件大小(HvLoadHive函数为它赋值),而在HHIVE中的HBASE_BLOCK.Length应该是一个BLOCK的大小4kb,10-17行做了转换。reactos中称这是一个hack,可见这种做法并不标准。
之后26-34行需要为每一个BLOCK(4kb)提供一个HMAP_ENTRY结构,存储在Hive.Storage[Stable]中。

Hive.Storage是Dual结构数组,Stable和Volatile各占用一个元素。
  1. typedefstruct _DUAL
  2. {
  3. ULONG Length;
  4. PHMAP_DIRECTORY Map;
  5. PHMAP_ENTRY BlockList;// PHMAP_TABLE SmallDir;
  6. ULONG Guard;
  7. HCELL_INDEX Freedisplay[24];//FREE_disPLAY Freedisplay[24];
  8. ULONG FreeSummary;
  9. LIST_ENTRY FreeBins;
  10. } DUAL,*PDUAL;
freeldr中主要用到了Length、BlockList和Freedisplay三个元素。Freedisplay用作快速查找空闲CELL,这个一会儿再说。Length代表这个DUAL维护了多少个Block。每个Block会有一个对应的HMAP_ENTRY结构。BlockList指向HMAP_ENTRY数组的地址。
HMAP_ENTRY结构为
  1. typedefstruct _HMAP_ENTRY
  2. {
  3. ULONG_PTR BlockAddress;
  4. ULONG_PTR BinAddress;
  5. struct _CM_VIEW_OF_FILE *CmView;
  6. ULONG MemAlloc;
  7. } HMAP_ENTRY,*PHMAP_ENTRY;
BlockAddress就是一个Block的开始地址,BinAddress是这个Block所在的Bin结构的开始地址,一个Block的大小是4kb,所以这里不需要类似Length的数据。
我们回到 HvpInitializeMemoryHive的26-67行。这里初始化了Storage[Stable]对应的DUAL结构。
26、27行首先计算出除 HBASE_BLOCK外,还有多少个Block,Dual.Length记录了Block的数量,之后为每一个Block生成HMAP_ENTRY,数组的首地址赋值给BlockList指针。

36行开始一个循环,初始化刚刚生成的HMAP_ENTRY数组。
在HIVE文件一个BIN占用若干,紧接着 HBASE_BLOCK后应是一个个的 HBIN结构,这是BIN的头部。
  1. typedefstruct _HBIN
  2. {
  3. ULONG Signature;
  4. HCELL_INDEX FileOffset; // BIN在文件中的偏移
  5. ULONG Size; // BIN的大小,是4kb的整倍数
  6. ULONG Reserved1[2];
  7. LARGE_INTEGER TimeStamp;
  8. ULONG Spare;
  9. } HBIN, *PHBIN;
FileOffset说明了该BIN在HIVE文件中的偏移,Size是该BIN的大小,这个大小肯定是4kb的整倍数。
39-46行首先检测了HBIN的Signature和Size,确定这时一个合法的BIN结构。
之后生成空间并复制BIN的全部内容(48-57)。
由于BIN可能会占用几个BLOCK,所以之后有一个循环(59-67)初始化这个BIN占用的所有BlOCK的HMAP_ENTRY结构,BlockAddress是该Block的 起始 地址,BinAddress是该BIN的起始地址。70行获得下一个BIN的地址,再次开始循环。
可以看出BIN占用的内存空间是同时生成的,所以BIN中所有BLOCK的内存也是连续的。

循环完毕后HMAP_ENTRY被初始化完成,函数调用 HvpCreateHiveFreeCellList初始化 DUAL.Freedisplay看先CELL链表,这个我们稍后看。
最后82-90初始化HIVE.DirtyVector,如果之后某一个块被更改了,位图中的相应位会被置位,注意每一个位都是按照Block(4kb)为单位进行管理的。初始化时这个位图全为0,说明没有任何更改。


现在来说说刚刚略过的HvpCreateHiveFreeCellList函数
BIN是一大块连续的内存(4kb的整倍数),这段内存又被分为了一个个叫做CELL的结构。
一个CELL就是一个CELL头HCELL和紧接着的一段内存。
  1. typedefstruct _HCELL
  2. {
  3. /* <0 if used,>0 if free */
  4. LONG Size;
  5. } HCELL, *PHCELL;
HCELL只有一个元素Size,代表这个CELL的大小(包括HCELL本身和紧接着的内存)。不过这个Size有点特殊,当Size小于0时说明这个CELL已经被使用,它的绝对值是真正的大小。当Size大于0说明这个CELL空闲,Size本身就是大小。
所有空闲的CELL都被放到了Dual.Freedisplay中,HvpCreateHiveFreeCellList就是这个初始化过程。
HvpCreateHiveFreeCellList以Hive指针作为参数,这时HIVE.Storage. BlockList 已经初始化好了。

lib/cmlib/Hivecell.c
  1. NTSTATUS CMAPI HvpCreateHiveFreeCellList(PHHIVE Hive)
  2. { ......
  3. /* 初始化Storage中的Freedisplay为空 */
  4. for(Index =0; Index <24; Index++)
  5. {
  6. Hive->Storage[Stable].Freedisplay[Index]= HCELL_NIL;
  7. Hive->Storage[Volatile].Freedisplay[Index]= HCELL_NIL;
  8. }
  9. BlockOffset =0;
  10. BlockIndex =0;
  11. /* 按Block开始循环 */
  12. while(BlockIndex < Hive->Storage[Stable].Length)
  13. { /* 获得BIN结构 */
  14. Bin =(PHBIN)Hive->Storage[Stable].BlockList[BlockIndex].BinAddress;
  15. /* 一个BIN里面每个CELL循环一次 */
  16. FreeOffset =sizeof(HBIN);
  17. while(FreeOffset < Bin->Size)
  18. { /* 获得HCELL指针 */
  19. FreeBlock =(PHCELL)((ULONG_PTR)Bin + FreeOffset);
  20. /* Size > 0 说明空闲,调用HvpAddFree把这个CELL加入空闲列表 */
  21. if(FreeBlock->Size >0)
  22. {
  23. Status = HvpAddFree(Hive,FreeBlock,Bin->FileOffset + FreeOffset);
  24. if(!NT_SUCCESS(Status))
  25. return Status;
  26. /* 下一个HCELL的地址 */
  27. FreeOffset += FreeBlock->Size;
  28. }
  29. else
  30. { /* 下一个HCELL的地址 */
  31. FreeOffset -= FreeBlock->Size;
  32. }
  33. }
  34. /* 下一个BIN */
  35. BlockIndex += Bin->Size / HV_BLOCK_SIZE;
  36. BlockOffset += Bin->Size;
  37. }
  38. return STATUS_SUCCESS;
  39. }
首先初始化Storage.Freedisplay,全部赋值为空(4-8行)。
12行开始遍历Storage[Stable].BlockList列表。
获得BIN结构后遍历里面一个个CELL,如果CELL.Size > 0 说明空闲,调用HvpAddFree加入空闲列表(14-33)。
完成后继续遍历下一个BIN(35-36)。

函数的关键在HvpAddFree中,这个函数有三个参数
Hive为HHIVE指针,
FreeBlock为空闲HCELL的指针,
FreeIndex是HCELL在文件中的偏移,这个参数之所以叫做FreeIndex是因为它也被用户当作这个CELL的索引(或句柄?)使用。
lib/cmlib/hivelcell.c
  1. staticNTSTATUS CMAPI HvpAddFree(PHHIVE RegistryHive,PHCELL FreeBlock,HCELL_INDEX FreeIndex)
  2. {
  3. PHCELL_INDEX FreeBlockData;
  4. HSTORAGE_TYPE Storage;
  5. ULONG Index;
  6. /* index的最高位代表Storage类型,1为Volatile,0为Stable */
  7. Storage =(FreeIndex & HCELL_TYPE_MASK)>> HCELL_TYPE_SHIFT;
  8. /* 计算出应该插入的Freedisplay表项 */
  9. Index = HvpComputeFreeListIndex((ULONG)FreeBlock->Size);
  10. /* 插入以Freedisplay[index]为表头的链表 */
  11. FreeBlockData =(PHCELL_INDEX)(FreeBlock +1);
  12. *FreeBlockData = RegistryHive->Storage[Storage].Freedisplay[Index];
  13. RegistryHive->Storage[Storage].Freedisplay[Index]= FreeIndex;
  14. /* FIXME: Eventually get rid of free bins. */
  15. /* 这里应该去除所有CELL都是free的BIN */
  16. return STATUS_SUCCESS;
  17. }
第三个参数还有一个特殊含义,最高位表示freecell的类型,因为HvpCreateHiveFreeCellList调用这个函数时是Stable类型的cell,所以最高位为0,索引正好等于CELL在文件中开始的位置。
函数之后调用HvpComputeFreeListIndex根据大小计算出需要插入的Freedisplay表头,这是是一个哈希函数,根据大小不同可以返回不同的Index值,但是唯一的Size每次返回的Index都相同,而且Index是随着Size递增的。这里就不仔细看了。 这个函数把不同大小的空闲CELL按策略分配在24个Freedisplay链表中。
之后把空闲的CELL插入到Storage[Storage].Freedisplay[Index]中,这是一个单项链表,连表中的空闲CELL需要存储下一个空闲CELL的Index,最后一个空闲CELL将存储HCELL_NIL为结束标志。

至此初始化完毕,初始化有几大部分
1. HHIVE结构
2. HBASE_BLOCK
3. HMAP_ENTRY数组
4. 空闲CELL列表Freedisplay

下一节我们就将看到如何分配CELL,如何通过INDEX找到CELL,HIVE文件如何增长等等。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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