$ uname -a
Linux CentOS8-Dev 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Tue Nov 16 14:42:35 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ zfs --version
zfs-2.1.99-530_g269b5dadc
zfs-kmod-2.1.99-530_g269b5dadc
vdevs(virtial devices)
:zfs的pool是由一组vdevs组成。vdev有物理虚拟设备和逻辑虚拟设备组成。zfs的pool可以看成是一棵树,树的非叶子节点是逻辑虚拟设备,叶子节点是物理虚拟设备。逻辑虚拟设备在指向由一组物理虚拟设备,物理虚拟设备则是具体的物理磁盘。例如创建一个zfs pool,pool中有2个互为镜像(M1和M2,M1和M2互为mirrors).M1包含了2个物理虚拟磁盘(dev-A和dev-B).M2包含了2个物理虚拟磁盘(dev-C和dev-D).pool的顶层是root vdev
.其数据结构把无关的字段去掉后,如下struct vdev {
// 当前vdev的唯一标识
uint64_t vdev_id;
// root vdev节点
vdev_t *vdev_top;
// 当前vdev的父节点
vdev_t *vdev_parent;
// 当前vdev的子节点数组
vdev_t **vdev_child;
// 当前vdev子节点的个数
uint64_t vdev_children;
}
整个vdev的结构如下图所示:
// 创建了2个pool,一个是cow
$ zdb cow
Cached configuration:
version: 5000
// pool的名称
name: 'cow'
state: 0
txg: 565
pool_guid: 1484685820301823750
errata: 0
hostname: 'CentOS8-Dev'
com.delphix:has_per_vdev_zaps
// 这里只有一个节点子节点
vdev_children: 1
// 虚拟设备的tree
vdev_tree:
// 这里是root vdev
type: 'root'
id: 0
guid: 1484685820301823750
create_txg: 4
// 当前子节点的vdev
children[0]:
type: 'disk'
id: 0
guid: 8957727234128580398
path: '/dev/sdb1'
devid: 'ata-CentOS8-Dev-0_SSD_WJMTS4R4TWPMF4J1N2KV-part1'
phys_path: 'pci-0000:00:1f.2-ata-3'
whole_disk: 1
metaslab_array: 67
metaslab_shift: 29
ashift: 12
asize: 68704272384
is_log: 0
create_txg: 4
vdev labels
:每个物理虚拟设备(叶子节点的磁盘)包含了256K的vdev_label
.vdev_label
包括具体物理设备的描述信息和其他共享top-level的vdev的物理设备。设计vdev_label有两个目的,第一个是为了访问pool的信息,同时用来校验pool的可用性。每个zfs pool会对每个物理虚拟设备写入4份vdev_label的信息,其中2份(0,2)写入到物理虚拟磁盘的头部;剩下2份(1,3)写入物理虚拟磁盘的尾部。 struct vdev_label
是用来描述vdev_label的信息,具体定义如下:typedef struct vdev_label {
// 填充
char vl_pad1[VDEV_PAD_SIZE]; /* 8K */
// boot header的信息
vdev_boot_envblock_t vl_be; /* 8K */
// 112K的name-value的kv结构,用来描述物理虚拟设备信息,比如之前的vdev结构中,在物理虚拟设备vdev-A的物理虚拟设备的vdev_label会包含vdev-A和vdev-B和M的信息
vdev_phys_t vl_vdev_phys; /* 112K */
// uberblock的结构,用来访问pool的,uberblock从来不会被重写,是采用cow的策略
char vl_uberblock[VDEV_UBERBLOCK_RING]; /* 128K */
} vdev_label_t;
下面展示了struct vdev_label
具体存储的信息
blkptr_t
结构是zfs用来描述数据块位置、校验。zfs的读写的数据洗是需要从逻辑虚拟设备到物理虚拟设备的抓换,zfs内部如果数据块超过128K则需要1个数据块以及多个数据块指针。blkptr_t
在zfs中定义如下:typedef struct blkptr {
// 给定文件名称来确定在vdev中的数据指针(包括了vdev + offset)
dva_t blk_dva[SPA_DVAS_PER_BP];
uint64_t blk_prop; /* size, compression, type, etc */
uint64_t blk_pad[2]; /* Extra space for the future */
// 当block被申请后txg的编号
uint64_t blk_phys_birth; /* txg when block was allocated */
// txg的编号
uint64_t blk_birth; /* transaction group at birth */
// block pointer数据指针的个数
uint64_t blk_fill; /* fill count */
// zfs会计算数据和元数据的cehcksum,存储在这个字段中
zio_cksum_t blk_cksum; /* 256-bit checksum */
} blkptr_t;
// 获取inode信息
$ ls -i /cow/fs1
11 data1
// 使用zdb来查看这个inode的数据块分布
$ zdb -dddddd cow/fs1 11
Dataset cow/fs1 [ZPL], ID 133, cr_txg 8, 104K, 9 objects, rootbp DVA[0]=<0:3c001b000:1000> DVA[1]=<0:3e001a000:1000> [L0 DMU objset] fletcher4 uncompressed unencrypted LE contiguous unique double size=1000L/1000P birth=17722L/17722P fill=9 cksum=d95af3266:2524e8bfa703:3623a469f762d6:37d3ce753d3b7f0c
Object lvl iblk dblk dsize dnsize lsize %full type
11 1 128K 512 4K 512 512 100.00 ZFS plain file (K=inherit) (Z=inherit=uncompressed)
184 bonus System attributes
dnode flags: USED_BYTES USERUSED_ACCOUNTED USEROBJUSED_ACCOUNTED
dnode maxblkid: 0
// data1文件的元数据
path /data1
uid 0
gid 0
atime Fri Dec 3 05:22:18 2021
mtime Fri Dec 3 05:22:18 2021
ctime Fri Dec 3 05:22:18 2021
crtime Fri Dec 3 05:22:18 2021
gen 17721
mode 100755
size 133
parent 34
links 1
pflags 840800000104
xattr 12
// 这里indirect blocks,这里就是个数据块
Indirect blocks:
// L0 是数据块
// 0 是vdev的编号;3a001c000是虚拟设备的offset.当前数据的是在vdev=0,offset=3a001c000的偏移量,F=1 是当前数据指针是1个,B=17721代表txg的number
0 L0 0:3a001c000:1000 200L/200P F=1 B=17721/17721 cksum=c49acd1a9:55c27b8b422:1301dda03a28f:2da540a668650a
segment [0000000000000000, 0000000000000200) size 512