虚拟硬盘是虚拟化的关键。当一个服务器通电时,每个虚拟机加载到服务器内存并从其相应的VHDX文件启动。随着虚拟机的运行,VHDX文件可通过更新来反映数据或状态改变。本质上来说,虚拟磁盘就是放在物理硬盘上的一个单独的文件。虚拟磁盘文件的目的是捕获驻留在服务器内存的虚拟机的完整状态,并将信息以一个已明确的磁盘文件格式显示出来。下面我们来说说虚拟硬盘VHDX的技术实现。
在前面的文章中曾经描述了VHD虚拟硬盘的文件格式以及寻址等技术,在本文下面部分将描述VHD虚拟硬盘的升级版本VHDX的文件格式以及寻址等技术。在VHDX虚拟硬盘中,也存在着和VHD虚拟硬盘同样的是三种类型:固定、动态以及差异。但是与VHD虚拟硬盘所不同的是,VHDX虚拟硬盘针对这三种类型的格式都是统一的格式架构。
首先我们介绍下VHDX的一些基本的信息:VHDX格式功能在操作系统的硬盘系统层以及操作系统的文件系统层提供,并经过优化,可与现代存储硬件配置配合使用。在操作系统的硬盘系统层,VHDX能够支持虚拟磁盘的大小高达64TB,以前的VHD只支持到2TB;支持的逻辑扇区大小高达4 KB,有助于将4 KB物理磁盘扇区转换为虚拟磁盘,并支持高达256 MB的虚拟磁盘的大数据块,使得我们可以调整数据块大小并与应用程序或系统的IO模式相匹配,也就是说,相比VHD,VHDX 提供更大的块容量和扇区容量,可以提供更高的性能。在操作系统的文件系统层,VHDX使用日志记录VHDX元数据结构的更新,可避免因电源故障等损坏数据,提供内置的保护能力;元数据区设立用户元数据区,能够存储有关用户的自定义元数据。同时,VHDX 还改进了虚拟硬盘的对齐方式,不但以此可以适应新型物理磁盘,还可以改进和优化VHDX文件的大小。而且VHDX格式的设计还为今后做了考虑,微软将来可以在此基础上方便的引入其他功能,也允许其他第三方的解析器实现对VHDX文件的扩展等等。
下面我们来看一下VHDX文件的架构:
与旧的VHD 格式相比,VHDX 的结构有了较大的改变。下图描述了VHDX 的逻辑布局架构。
从上图我们可以看到,VHDX文件和VHD文件的结构有很大的区别:
1)、VHDX 没有VHD中的页脚区域;
2)、增加了VHD所没有的日志区域;
3)、增加了元数据区域,其内设置了系统元数据区域和用户元数据区域;
如果看这个比较抽象,那么我们看看VHDX文件的文件布局图:
如上图所示,一个VHDX文件总体上可以由三个部分组成:
1、固定大小的头部区域(1MB)
2、非重叠的对象
3、自由空间
VHDX文件以固定1MB大小的头部区域开头。此后,非重叠对象和自由空间以任何特定顺序自由组合; 唯一的限制是所有对象在文件中需要按照1 MB大小对齐。非重叠对象的意思就是当前定义的这些对象:块分配表(也称为BAT),元数据区域,日志,有效载荷块(数据块)和扇区位图块。这些对象不能相互重叠,比如元数据区域和元数据区域之间不能连着在一起,只要对象不重叠并且保持MB对齐,就可以在文件中自由移动对象。
例如,其中一个VHDX文件:
由上图所示:VHDX文件的结构由头部、日志、块分配表(也称为BAT)、扇区位图、数据块(有效载荷块)、自由空间、元数据区等这样的结构组成。在后面的部分,自由空间和这些对象进行了自由组合。
下面将详细讲解这些组成对象:
1、首部区域
VHDX文件的头部区域是虚拟硬盘上的第一个对象,是打开VHDX文件时首先检查的结构。头部大小为1 MB,包含五个大小为64 KB的项目:文件类型标识符,两个标头和两个区域表。
其文件布局如下图所示:
1.1、文件类型标识符
文件类型标识符占64KB大小。包含一个简短的固定文件签名和一个文件创建者字段,文件签名字段必须为0x656C696678646876(“vhdxfile”为ASCII),用以将文件标识为VHDX;文件创建者字段用于标识VHDX文件的创建者。其结构体如下图所示:
文件类型标识符存储在从VHDX文件偏移量为0开始的前64KB中。该区域不会被覆盖,确保即使因为写入失败破坏了文件的扇区,该文件仍然可以被识别为VHDX。
文件签名字段包含了描述文件类型是VHDX的一个UINT64结构体。文件创建者字段包含描述创建VHDX文件的解析器的UTF-16字符串。此字段不能为空并且是可选的,如果没有填入的情况下,默认置零值。该字段是解析器在创建VHDX文件的时候,填充的一个唯一可以标识VHDX文件创建者的字符。这个字段在目的是用于解析器在出现错误的时候读取该字段来进行诊断问故障问题的,不会使用此字段影响解析器的行为。
解析器必须在创建文件时写入文件类型标识符结构,并且在加载VHDX文件时必须验证签名字段。在创建文件后,解析器不能覆盖文件的前64 KB中的任何数据。
1.2、标头(Headers)
每个标头占64KB大小,两个头部区域中的每一个标头都是相同的。一个标头存储在偏移量为64 KB处开始的地方,另一个标头存储在128 KB处开始的地方。标头的作用是用来定位日志的,因此无法通过日志对标头进行更新。为了提供电源故障一致性保证,每个VHDX文件中都有两个标头。
标头作为VHDX根数据结构树,提供版本信息,日志的位置和大小以及一些基本的文件元数据。其结构体如下图所示:
如上图,标头本身包含了签名、校验和、序列号、文件写入全局唯一标识符、数据写入全局唯一标识符、日志全局唯一标识符、日志版本、版本、日志长度、日志偏移以及保留部分组成。除了标识标头本身以及日志的信息外,标头本身还包含了基于本身HA机制的序列号和校验和等元数据,这些数据只用于标头本身的高可用。上述我们说明了,头部区域包含了两个标头,因此在这里,头部区域中每次只有一个标头处于活动状态,另外一个标头处于待命状态。这样的机制可以使得VHDX的标头增加安全性,当当前使用的标头遭到破坏的时候,可以安全地使用或覆盖另一个标头,从而保证VHDX文件不被破坏。因此在标头中,每个标头都额外包含了序列号和校验和来确保这种机制。
下面我们详细说明每个字段:
1)、签名字段:签名字段必须为0x64616568(“head”ASCII表示)。该字段标识其为一个标头。
2)、校验和字段:校验和字段是一个CRC-32C哈希结构,是对4 KB为单位结构利用CRC-32C哈希出来的值。该字段不为空,在计算校验和值期间取值为零。
3)、序列号字段:是一个64位无符号整数。如果签名和校验和都正确验证,则表示标头是有效的。如果标头是有效的标头并且其序列号字段大于另一个标头的序列号字段,则该标头是处于活动状态的标头。解析器通过读取序列号的值来确定使用当前那个标头的数据。如果序列号值相同或者没有值,则无法确定当前的活动标头,则解析器会认为VHDX文件已损坏。
4)、文件写入全局唯一标识符字段:该字段指定一个128位全局唯一标识符来标识文件写入的内容。在每次打开VHDX文件时,解析器必须在对文件进行第一次修改之前将此全局唯一标识符更改为新的全局唯一标识符,包括系统和用户元数据以及日志回收。如果存储文件的存储介质为只读,或者该文件在只读模式下打开,解析器则会跳过此字段。
5)、数据写入全局唯一标识符字段:该字段指定一个128位的全局惟一标识符来标识用户可见数据的内容。在每次打开VHDX文件时,解析器必须对用户可见数据进行第一次修改之前将此字段更改为新的全局唯一标识符。如果虚拟磁盘的用户通过读取虚拟磁盘来进行数据的更改,则解析器必须更新此字段。这包括更改系统和用户元数据,原始块数据,磁盘大小或数据块转换的状态。因为如果不更新这个字段,将导致虚拟磁盘扇区读取与先前读取的数据不同,因为数据已经发送更改操作,可能新的数据块已经存储到了别的地方。值得注意的是,这不包括文件中数据块的移动,因为数据块的移动仅仅是改变文件的物理布局,而不是虚拟磁盘的布局。数据写入全局唯一标识符字段用于差异VHDX链的完整性验证。解析器必须特别注意以确保它们按照所述进行更新。
6)、日志全局唯一标识符字段:该字段指定一个128位的唯一标识符用于确定日志条目的有效性。如果此字段为零,则日志为空或没有有效条目,不能使用日志进行数据恢复。只有在标头中包含此标识符的日志条目时才是有效的日志条目。打开时,解析器必须在覆盖日志区域中的现有空间之前将此字段更新为新的非零值。
7)、日志版本字段:该字段指定VHDX文件中使用的日志格式的版本。 此字段必须设置为零。 如果不是,解析器不能继续解析文件,除非日志全局唯一标识符字段为零,表示没有要回放的日志。
8)、版本字段:该字段指定VHDX文件中使用的VHDX格式的版本。此字段必须设置为1。如果不是,解析器就不能使用此格式规范中的详细信息来解析文件。
9)、日志长度和偏移量字段:该字段指定文件中的字节偏移量和日志的长度。这些值必须是1 MB的倍数,而且日志偏移量必须至少1 MB大小。
10)、保留字段:保留字段定义了剩余的空间的区域地址信息。
上述就是处于头部区域中标头的介绍部分。我们说在头部区域中存在着两个标头作为HA机制而存在,而且由于VHDX格式中固定、动态以及差异都是同一种结构。因此随着数据的是变化,比如数据的动态增加,其头部区域的标头部分所记录的键值也需要进行随时更新。下面我们来说说头部区域中标头的更新机制。
在每次打开VHDX文件并允许写入VHDX文件时,必须在修改文件的之前(文件的任何部分)更新标头。同时,在使用VHDX文件时,当虚拟硬盘或VHDX文件上的操作影响标头中包含的字段时,也需要更新标头。
第一次在打开VHDX文件后更新标头,解析器必须为文件写入全局唯一标识符字段生成新的随机值。在用户请求可能影响虚拟磁盘内容或用户可见虚拟磁盘元数据(如磁盘大小或扇区大小)的操作之前,解析器不应更新DataWriteGuid字段。
解析器可以遵循以下过程将非当前报头改变为当前报头:
1.标识当前标头和非当前标头。
2.在内存中生成一个新标题。将SequenceNumber字段设置为当前标头的SequenceNumber字段加一。
3.根据需要将其他字段设置为其当前值或更新值。如果这是会话中的第一个头更新,请为FileWriteGuid使用一个新值。
4.检查内存中的标头。
5.使用内存中头部覆盖文件中的非当前头部。发出flush命令以确保主机磁盘存储介质上的标题更新是稳定的。
在此过程之后,非当前头成为当前头。解析器应该再次执行更新过程,以便当前和非当前头部都包含最新的信息;这确保如果一个头损坏,该文件仍然可以打开。
1.3、区域表
区域表列出了文件中的虚拟连续,可变大小,MB对齐的数据片段的区域。这些对象当前包括BAT和元数据区域,但是可以通过解析器或未来对规范的修订来扩展,而不破坏不同解析器的实现和版本的兼容性。解析器必须保持他们不理解的对象而不破坏它们。解析器必须无法打开包含标记为必需但该解析器不理解的区域的VHDX文件。
区域表由一个标题后跟可变数量的条目组成,这些条目指定文件中区域的标识和位置。区域表的两个副本存储在文件偏移量192 KB和256 KB。必须通过日志更新区域表结构。
其中区域表的头部包含以下内容:
签名字段必须为0x69676572。
校验和字段是整个64 KB表上的CRC-32C哈希值,该字段在计算校验和值期间,默认取值为零,直到算出校验和为止。
条目数字段指定要遵循的有效条目的数量。这个值必须小于或等于2047。
而区域表的后半段区域表条目部分,其组成结构如下所示:
Guid字段指定对象的128位标识符,并且必须是唯一值。
文件偏移量字段和长度字段指定文件中对象的64位字节偏移量和32位字节长度。这些值必须是1 MB的倍数,其中文件偏移量字段必须至少为1 MB。
在该表中的所有对象不能够重叠,不仅相对于区域表条目内部彼此之间,也相对于日志(在头部中定义)和数据块和扇区位图块(在BAT中定义)。
必需字段指定该区域是否必须由解析器识别才能加载VHDX文件。 如果此字段的值为1,并且解析器无法识别此区域,则解析器必须拒绝加载VHDX文件。
保留区域表记录的是保留的空间地址段。
下表总结了VHDX规范中定义的区域的属性。
未完待续...