(翻译 《ZFS On-Disk Specification》, 由于是2006年给出的文档,与当前ZFS系统肯定有很多的不同,但是也是一份相当有帮助的ZFS学习文档)
数据在主存和磁盘知己恩的传输单元称为“块(block)”。ZFS中,块指针(blkptr_t)是一个128字节的结构体,用来定位磁盘上数据的物理位置、验证其合法性以及描述该数据。
128字节的blkptr_t结构体的布局如下图所示:
2.1 DVA(Data Virtual Address)
数据的虚拟地址(DVA)是有块指针中的vdev和offset两个部分构成的。比如vdev1和offset1构成了一个DVA(dva1),ZFS的块指针提供3份DVA,三份DVA所指向的数据是相同的。在实际使用过程中,使用的DVA个数表示了这个块指针的宽度:使用一个DVA则是单宽度块指针;使用两个DVA则是双宽度块指针;使用三个DVA则是三宽度块指针。
每个DVA的vdev部分是由一个32位的整数表示,用来唯一标识包含这个块的vdev ID,DVA的offset部分是由一个63位的整数表示,用来表示这个块在vdev所指定的设备内的偏移(从L0和L1以及Boot block之后开始算起,即4M之后),这样通过vdev和offset组合,来唯一表示数据块在的位置。
offset中存储的值是一扇区(512bit的块)来计算的,要想得到数据块激励磁盘开头的位置的具体字节数,首先要将offset中的数据左移9位(29=512)然后再将得到的值加上0x400000(4M)。
physical block address = (offset << 9) + 0x400000(4M)
2.2 GRID
Raid-Z的布局信息,留着以后使用
2.3 GANG
集群块(gang block)指的是这个块的内容包含的是块指针。当磁盘上的剩余空间不足以分配一个连续的指定空间大小时就需要使用gang block。当这种情况发生时,一些小的块将会被分配(小块的空间总和是要分配的空间大小),而gang block将包含这些小块的块指针,然后将gang block的块指针返回给请求者,在请求者看来,这是一个完整的块。
Gang block是通过“G”标志位表明。
"G"标志值 | 描述 |
0 | 不是gang block |
1 | 是gang block |
gang block的大小为512字节,并且自身checksum。gang block中, 32字节的紧随是3个块指针之后。下面给出gang block的具体描述。
typedef struct zio_gbh{
blkptr_t zg_blkptr[SPA_GBH_NBLKPTRS];
uint64_t zg_filler[SPA_GBH_FILLER];
zio_block_tail_t zg_tail;
}zio_gbh_phys_t;
zg_blkptr:块指针数组。每个512字节的gangblock有3个块指针。
zg_filler:用来字节对齐
typedef struct zio_block_tail{
uint64_t zbt_magic;
zio_cksum_tzbt_cksum;
}
zbt_magic:魔数,值为0x210da7ab10c7a11(zio-data-blc-tail)
2.4 checksum
默认情况下,ZFS checksum所有的数据以及元数据,ZFS支持fletcher2、fletcher4以及SHA-256在内的多种checksum算法。下表给出块指针中的8位cksum指定的checksum算法类型:
描述 | 值 | 算法 |
on | 1 | fletcher2 |
off | 2 | one |
label | 3 | SHA-256 |
gang header | 4 | SHA-256 |
zilog | 5 | fletcher2 |
fletcher2 | 6 | fletcher2 |
fletcher4 | 7 | fletcher4 |
SHA-256 | 8 | SHA-256 |
2.5 压缩每个数据都将会被按照块指针中的cksum制定的算法计算一个256位的checksum值。如果cksum的值是2,那么将不会计算checksum,块指针中的checksum[0], checksum[1], checksum[2], checksum[3]都为0,否则256位的checksum值将会被分别存储到这四个checksum中。
ZFS支持多种压缩算法。具体的压缩算法通过块指针的comp字段指定。
描述 | 值 | 算法 |
on | 1 | lzjb |
off | 2 | none |
lzjb | 3 | lzjb |
2.6 块大小
块的大小有三种描述,分别为psize、lsize、asize。
lsize:逻辑大小,没有经过压缩、raidz或gang覆盖的数据的大小。
psize:数据压缩之后,在磁盘上的存储空间。
asize:分配空间大小。用于存储该数据的所分配的所有块的总大小,包括gang header等。
如果关闭ZFS的压缩功能,而且没有使用Raid-Z存储,那么lsize、psize和asize应该是相同的值。
所有的大小都以512字节扇区的形式来存储。(size >> 9 之后存储)
2.7 字节模式
ZFS是一个可调节字节模式的文件系统,这使得数据可以在不同架构的机器之间传输。一般来说,在写block的过程,会按照当前机器的字节模式来写。
字节模式 | 值 |
小端模式 | 1 |
大端模式 | 0 |
如果一个pool被移动到另一个字节模式不同的机器上时,读取的过程需要交换字节。
2.8 类型
块指针中的type字段用来表示所指的数据块的类型。type可以是以下类型,第三章中将会详细介绍对象的类型。
类型 | 值 |
DMU_OT_NONE | 0 |
DMU_OT_OBJECT_DIRECTORY | 1 |
DMU_OT_OBJECT_ARRAY | 2 |
DMU_OT_PACKED_NVLIST | 3 |
DMU_OT_NVLIST_SIZE | 4 |
DMU_OT_BPLIST | 5 |
DMU_OT_BPLIST_HDR | 6 |
DMU_OT_SPACE_MAP_HEADER | 7 |
DMU_OT_SPACE_MAP | 8 |
DMU_OT_INTENT_LOG | 9 |
DMU_OT_DNODE | 10 |
DMU_OT_OBJSET | 11 |
DMU_OT_DSL_DATASET | 12 |
DMU_OT_DSL_DATASET_CHILD_MAP | 13 |
DMU_OT_OBJSET_SNAP_MAP | 14 |
DMU_OT_DSL_PROPS | 15 |
DMU_OT_DSL_OBJSET | 16 |
DMU_OT_ZNODE | 17 |
DMU_OT_ACL | 18 |
DMU_OT_PLAIN_FILE_CONTENTS | 19 |
DMU_OT_DIRECTORY_CONTENTS | 20 |
DMU_OT_MASTER_NODE | 21 |
DMU_OT_DELETE_QUEUE | 22 |
DMU_OT_ZVOL | 23 |
DMU_OT_ZVOL_PROP | 24 |
2.9 level
块指针中的level字段用来表明要想找到真正的数据需要经历几层块指针。第三章中将更详细地说明
2.10 Fill
块指针中的fill字段用来描述当前块指针中的非零块指针的数目。对于数据块的块指针来说,该值为1。
fill字段在指向DMU_OT_DNODE类型的时稍微有点不同,它用来表示这个块指针下的空闲dnode数目。
2.11 Birth Transaction
块指针中的“birth txg”字段是一个64位的整数,用来记录分配这个块指针的事务组编号。
2.12 Padding
块指针中还有三个padding字段,是保留空间,以后可能使用。
最后奉献一张块指针的大图: