治肾虚不含糖,专注内核性能优化二十年。 https://github.com/KnightKu
分类:
2018-08-28 15:32:02
原文地址:ZFS代码简介(2) 作者:qincp-
DMU基于SPA提供的平坦地址空间,提供事务对象模型。用户通过对象集,对象,事务与DMU交互。对象集是对象的一个集合,其中的每个对象是SPA中的任意一块存储空间。事务是一系列必须作为一个组提交到磁盘的操作,对于ZFS磁盘上数据一致性来说,事务是一个核心概念。
dmu.c |
DMU主要外部接口 |
dmu_objset.c |
对象集打开/关闭/操作外部接口 |
dmu_object.c |
对象分配释放接口 |
txg.c |
事务模型控制线程 |
dmu_tx.c |
事务创建/操作接口 |
dnode.c |
打开上下文对象集操作函数 |
dnode_sync.c |
同步上下文对象级操作函数 |
dbuf.c |
缓存管理函数 |
dmu_zfetch.c |
与拘留预取逻辑 |
refcount.c |
通用引用计数器接口 |
dmu_send.c |
快照发送和接收函数 |
DSL在一个层次结构的命名空间中将DMU对象集进行组合,DSL有自己可继承的属性,配额,强制保留空间等。DSL同时负责对象集的快照和克隆的管理。
快照实现的更多信息,可以参见Matt的[c1] 。
dsl_dir.c |
命名空间管理函数 |
dsl_dataset.c |
支持 快照/回滚/克隆接口 |
dsl_pool.c |
存储池支持接口 |
dsl_prop.c |
属性操作函数 |
unique.c |
唯一对象集ID支持函数 |
ZAP基于DMU,使用可扩展hash算法创建对象集相关的不同属性(名称,对象)。ZAP常用于ZPL中的目录实现,在DSL中的其他地方也广泛使用,ZAP也作为存储池属性管理的方法。有两种不同的ZAP算法,用于不同类型的目录。“微ZAP”用于条目较少,每个条目内容较短的情况;“肥ZAP”用于较大目录,或者有非常长名称的属性管理。
zap.c |
肥 ZAP 接口 |
zap_leaf.c |
底层支持函数 |
zap_micro.c |
微ZAP接口 |
ZFS遵循传统文件系统语法,数据主体并不立即写入磁盘,否则性能会急剧下降。一些应用对数据安全有严格要求,要求在read()和write()操作返回时,系统必须保证数据已经存在磁盘上。为满足这类应用(带O_DSYNC标识),ZIL为数据集提供高效事务日志,在系统崩溃时可以进行回放,提供始终一致的磁盘数据。
ZIL实现的更多细节,请参见[c2] 的博客。
zil.c |
意图日志 |
Traversal提供了一个安全,高效,可重复的遍历存储池中所有数据的方法。该机制构成了resilvering和scrubbing的基础。它遍历所有元数据,查找给定时间段内的块修改。得益于ZFS的写时拷贝属性,可以快速排除故障期间未变动的子树。Tranversal基本上是一个SPA的机制,但为了处理快照,克隆以及其他一些磁盘格式相关特性,需要实际关注DMU的数据结构。
dmu_traverse.c |
Traversal 代码 |
ZFS使用ARC的一个修改版本,提供所需要的基本缓存。缓存位于DMU和SPA之间,工作在虚拟块层,因此允许文件系统在快照和克隆之间共享缓存数据。
arc.c |
自适应替代缓存实现 |
整个存储池层常称为SPA(存储池分配器),而其中的配置部分只是一个公开接口。负责将ZIO和vdev层整合为一个一致的池对象。包含创建,销毁池,以及定期从vdevs同步数据等一系列函数。
spa.c |
打开,导入,导出,销毁存储池的函数集 |
spa_misc.c |
各种SPA函数,包括锁 |
spa_config.c |
解析和更新存储池的配置数据 |
spa_errlog.c |
全存储池内的数据错误日志 |
spa_history.c |
成功修改存储池状态命令的环形缓存 |
zfs_fm.c |
发送FMA消费报告的函数 |
ZIO管道是所有数据出入磁盘的必经通道。ZIO负责DVA(设备虚地址)和vdev的逻辑地址之间的翻译工作,以及效验和,压缩数据的计算工作。ZIO设计为多阶段的管道,使用一个位掩码控制每个IO的执行阶段。管道本身非常复杂,可以大致总结为下图:
I/O types |
ZIO state |
Compression |
Checksum |
Gang Blocks |
DVA management |
Vdev I/O |
RWFCI |
open |
|
|
|
|
|
RWFCI |
wait for |
|
|
|
|
|
-W--- |
|
write compress |
|
|
|
|
-W--- |
|
|
checksum generate |
|
|
|
-WFC- |
|
|
|
gang pipeline |
|
|
-WFC- |
|
|
|
get gang header |
|
|
-W--- |
|
|
|
rewrite gang |
|
|
--F-- |
|
|
|
free gang |
|
|
---C- |
|
|
|
claim gang |
|
|
-W--- |
|
|
|
|
DVA allocate |
|
--F-- |
|
|
|
|
DVA free |
|
---C- |
|
|
|
|
DVA claim |
|
-W--- |
|
|
gang checksum |
|
|
|
RWFCI |
ready |
|
|
|
|
|
RW--I |
|
|
|
|
|
I/O start |
RW--I |
|
|
|
|
|
I/O done |
RW--I |
|
|
|
|
|
I/O assess |
RWFCI |
wait for |
|
|
|
|
|
R---- |
|
|
checksum verify |
|
|
|
R---- |
|
|
|
read gang |
|
|
R---- |
|
read decompress |
|
|
|
|
RWFCI |
done |
|
|
|
|
|
每一阶段的IO管道应用于特定IO类型。这些字符含义为:R(读),W(写),F(释放),C(要求),I(Ioctl)。
用于同步IO的内部状态。例如一个包含子IO的IO在申请BP前,必须等待所有的孩子就绪,在返回前必须等待所有孩子完成。
该阶段适用于所有压缩算法。
该阶段适用于所有效验和算法。
没有足够连续空间写入一个大的数据块时,ZIO管道将IO拆分为小的“Gang块”,而这个拆分对用户透明,表现为一个完整的块。
每个IO必须定位到设备虚地址(DVA),对应池中某个vdev的特定位置。这个阶段使用metaslab和空间映射,进行这些地址的翻译和定位。
这个阶段将IO实际提交给池中的vdev。
zio.c |
主ZIO阶段,包括Gang块翻译 |
zio_checksum.c |
通用效验和接口 |
fletcher.c |
Fletcher2和fletcher4效验和算法 |
sha256.c |
SHA256效验和算法 |
zio_compress.c |
通用压缩接口 |
lzjb.c |
LZJB压缩算法 |
uberblock.c |
基本uberblock函数 |
bplist.c |
块指针链表 |
metaslab.c |
DVA翻译 |
space_map.c |
空闲空间管理,在DVA翻译时使用 |
szio_inject.c |
Framework for injecting persistent errors for data and devices数据和设备的框架 |
虚设备子系统提供一个安排和访问设备的统一方法。池中的虚设备构成虚设备树,树有一个根vdev,多个内部(镜像,RAIDz)vdev,以及叶(磁盘,文件)vdev。每个vdev负责可用空间的管理,以及物理磁盘上块的格式分布。
vdev.c |
通用vdev函数 |
vdev_disk.c |
磁盘虚拟设备 |
vdev_file.c |
文件虚拟设备 |
vdev_mirror.c |
N路镜像 |
vdev_raidz.c |
RAIDZ组 (参见Jeff的博[c3] ). |
vdev_root.c |
根vdev |
vdev_missing.c |
特殊设备 |
vdev_label.c |
读写标签的函数集 |
vdev_cache.c |
读操作使用的简单设备层缓存 |
vdev_queue.c |
Vdev的I/O调度算法 |
在栈的底部,ZFS通过LDI和物理设备交互(在叶vdev为文件时,通过VFS接口)。