分类:
2009-09-22 16:01:14
ZFS意图日志在内存中保存了导致文件系统发生变更的系统调用的事务记录,使得系统可以重做这些事务。这些日志保存在内存中,直到DMU事务组将其提交到稳定的pool,或者由fsync,O_DSYNC或者其他同步请求刷新到稳定的日志中。
每个文件系统有一个ZIL。磁盘格式由三部分组成:ZIL头,ZIL块,ZIL记录。
一个日志记录包含一个系统调用事务。一个日志块可以保存多个日志记录。所有日志块组成一个链表,每一个ZIL块在尾部包含一个块指针(blkptr_t)指向链表中的下一个块,ZIL头指向链表中的第一个块。日志块可以有不同大小。注意块在pool中的位置并不固定,他们根据需要从pool中动态申请和释放。下图说明了ZIL结构的组织。
图 19 ZIL结构
下面更细致的对当前磁盘上的ZIL结构进行讨论。
每个ZIL有一个头,定义如下:
typedef struct zil_header {
uint64_t zh_claim_txg; /* 日志块申请时的事务组号 */
uint64_t zh_replay_seq; /* 最高中继序列号*/
blkptr_t zh_log; /* 日志块链*/
} zil_header_t;
ZIL块包含ZIL记录,块的大小按需申请,blkptr_t中有size字段表示指向的块大小。每个块以zil_trailer_t结尾。
ZIL Trailer
typedef struct zil_trailer {
blkptr_t zit_next_blk; /* 链表中下一个块 */
uint64_t zit_nused; /* 志块中使用的字节数 */
zio_block_tail_t zit_bt; /* 块尾 */
} zil_trailer_t;
ZIL 记录
ZIL 记录通用结构
ZIL 都以一个通用日志结构头开始,后面跟随记录(事务)特定结构。通用日志记录结构和记录对象如下:
typedef struct { /* 通用日志记录头 */
uint64_t lrc_txtype; /* intent log transaction type */
uint64_t lrc_reclen; /* transaction record length */
uint64_t lrc_txg; /* dmu transaction group number */
uint64_t lrc_seq; /* intent log sequence number */
} lr_t;
#define TX_CREATE 1 /*创建文件 */
#define TX_MKDIR 2 /* Make directory */
#define TX_MKXATTR 3 /* Make XATTR directory */
#define TX_SYMLINK 4 /* Create symbolic link to a file */
#define TX_REMOVE 5 /* Remove file */
#define TX_RMDIR 6 /* Remove directory */
#define TX_LINK 7 /* Create hard link to a file */
#define TX_RENAME 8 /* Rename a file */
#define TX_WRITE 9 /* File write */
#define TX_TRUNCATE 10 /* Truncate a file */
#define TX_SETATTR 11 /* Set file attributes */
#define TX_ACL 12 /* Set acl */
ZIL 记录特定结构
上面列出的每种记录类型,都有一个特定的结构对应。每个记录中都保存着可以重做事务(通道是一个VOP调用)的足够信息。VOP层可能传递内存指针给vnode,这些内容需要转换为稳定的pool对象标识(oid)。当重做事务时,VOP层被再次调用,重新打开对象,传递对象的vnode。
一些记录特定结构用于多个事务类型,lr_create_t就对应TX_CREATE, TX_MKDIR,
TX_MKXATTR 以及 TX_SYMLINK,lr_remove_t对应TX_REMOVE 和 TX_RMDIR。所有的域都是64位长度,这提供了各种架构上的对齐和方便的字节序转换。
下面是记录特定结构的定义:
typedef struct {
lr_t lr_common; /* 通用日志记录头 */
uint64_t lr_doid; /* 目录对象id */
uint64_t lr_foid; /* 被创建文件对象的对象id */
uint64_t lr_mode; /* 对象模式 */
uint64_t lr_uid; /* 对象uid */
uint64_t lr_gid; /* 对象的gid */
uint64_t lr_gen; /* 创建的事务组编号 */
uint64_t lr_crtime[2]; /* 创建时间 */
uint64_t lr_rdev; /* 创建对象的rdev */
/* 要创建的对象名跟在这里 */
/* 对于符号链接,对象名后跟随链接内容 */
} lr_create_t;
typedef struct {
lr_t lr_common; /* 通用日志记录头 */
uint64_t lr_doid; /* 目录对象id */
/* 要删除的对象名跟在这里 */
} lr_remove_t;
typedef struct {
lr_t lr_common; /*通用日志记录头 */
uint64_t lr_doid; /* 目录对象id */
uint64_t lr_link_obj; /* 链接对象id */
/* 链接对象的名称跟随在这里 */
} lr_link_t;
typedef struct {
lr_t lr_common; /* 通用日志记录头*/
uint64_t lr_sdoid; /* 原目录的对象id */
uint64_t lr_tdoid; /* 目的目录的对象id */
/* 两个字符串:源和目的的名称 */
} lr_rename_t;
typedef struct {
lr_t lr_common; /*通用日志记录头*/
uint64_t lr_foid; /* 写文件对象 */
uint64_t lr_offset; /* 写位置偏移 */
uint64_t lr_length; /* 写的用户数据长度 */
uint64_t lr_blkoff; /* lr_blkptr结构的偏移 */
blkptr_t lr_blkptr; /* spa重做块指针 */
/* 对于小的写操作,数据跟随在这里 */
} lr_write_t;
typedef struct {
lr_t lr_common; /* 通用日志记录头*/
uint64_t lr_foid; /* 截断文件对象id */
uint64_t lr_offset; /* 开始截断位置偏移 */
uint64_t lr_length; /* 截断长度 */
} lr_truncate_t;
typedef struct {
lr_t lr_common; /*通用日志记录头*/
uint64_t lr_foid; /* 更改属性的文件对象 */
uint64_t lr_mask; /* 要设置的属性掩码 */
uint64_t lr_mode; /* 要设置的模式 */
uint64_t lr_uid; /* 要设置的uid */
uint64_t lr_gid; /* 要设置的gid */
uint64_t lr_size; /* 要设置的尺寸 */
uint64_t lr_atime[2]; /* 访问时间 */
uint64_t lr_mtime[2]; /* 修改时间 */
} lr_setattr_t;
typedef struct {
lr_t lr_common; /*通用日志记录头*/
uint64_t lr_foid; /* 文件对象id */
uint64_t lr_aclcnt; /* acl条目数 */
/* lr_aclcnt个ace_t跟在这里 */
} lr_acl_t;
ZVOL提供一个创建逻辑卷的机制。ZFS 卷对外表现为一个块设备,可以和其他类型的块设备一样使用。ZFS中,ZVOL是一个类型为DMU_OST_ZVOL的对象集。ZVOL对象集格式非常简单,包含两个对象:一个属性对象和一个数据对象,分别为DMU_OT_ZVOL_PROP 和DMU_OT_ZVOL类型,两个对象都有静态的对象id。每个对象描述如下:
ZVOL属性对象
类型: DMU_OT_ZVOL_PROP
对象编号: 2
描述: ZVOl属性对象是一个ZAP对象,包含卷相关的属性。一个特别的属性是“volsize”,包含卷的大小,以字节为单位。
ZVOL 数据
类型: DMU_OT_ZVOL
对象编号: 1
描述: 存储虚块设备的内容