治肾虚不含糖,专注内核性能优化二十年。 https://github.com/KnightKu
分类:
2018-08-28 15:29:15
原文地址:ZFS磁盘格式(5) 作者:qincp-
ZAP(ZFS属性处理器)是DMU之上的模块,对ZAP类型的类型的对象进行操作。ZAP独享是一种DMU对象,用于存储以name/value对表示的属性。属性的Name部分是一个以’\
ZAP对象用于存储数据集的属性,文件系统对象导航,存储pool属性等。下标列出ZAP对象的类型。
表13 ZAP对象类型
ZAP Object Type |
DMU_OT_OBJECT_DIRECTORY |
DMU_OT_DSL_DIR_CHILD_MAP |
DMU_OT_DSL_DS_SNAP_MAP |
DMU_OT_DSL_PROPS |
DMU_OT_DIRECTORY_CONTENTS |
DMU_OT_MASTER_NODE |
DMU_OT_DELETE_QUEUE |
DMU_OT_ZVOL_PROP |
ZAP对象有两种形式:微ZAP对象和肥ZAP对象。微ZAP对象是肥ZAP对象的轻量版本,包含小量的属性,提供一个简单和快速查找机制。而肥ZAP对象类型比较适合包含大量属性的ZAP对象。
ZFS使用下面几个规则来决定使用肥ZAP还是微ZAP:
如果下面三个条件全部满足,则使用微ZAP:
1. 所有name/value对可以放入一个块。ZFS最大数据块大小为128K,这样大小的块可以放2047微ZAP条目。
2. 所有属性的值是uint64_t类型。
3. 每个属性的名称的长度小于或者等于50个字符(包含‘\0’)
如果其中之一不满足,则需要使用肥ZAP。
ZAP对象的每个块的前64位用于表示这个块包含的ZAP内容的类型。下表列出了这些类型。
表14 ZAP对象块类型
类型 |
描述 |
值 |
ZBT_MICRO |
块包含了微ZAP条目 |
(1ULL << 63) + 3 |
ZBT_HEADER |
块用于肥ZAP。这个标志仅用于肥ZAP的第一个块。 |
(1ULL << 63) + 1 |
ZBT_LEAF |
块用于肥ZAP,这个标志用于除第一个块外的其他块 |
(1ULL << 63) + 0 |
微ZAP为存储少量属性实现了一个简单机制。微ZAP对象由一个块组成,包含一个微ZAP条目(mzap_ent_phys_t结构)数组。每个存储在微ZAP对象中的属性由这个数组中的一个元素表示。
微ZAP块以这样的方式组织:块的头128字节包含一个微ZAP头结构,mzap_phys_t。这个结构包含一个64位的ZBT_MICRO指示这个块用户存储微ZAP条目;这后面是一个64位salt值,该值传入hash以使得每一个ZAP对象的hash函数都是不同的;再后面是42字节的空间保留;最后64字节包含第一个微ZAP条目(mzap_ent_phys_t类型)。块中剩下的空间存储一个mzap_ent_phys_t结构数组。下图展现了块的结构:
图15 微ZAP块结构
Mzap_ent_phys_t结构定义如下。
#define MZAP_ENT_LEN 64
#define MZAP_NAME_LEN (MZAP_ENT_LEN - 8–4 - 2)
typedef struct mzap_ent_phys {
uint64_t mze_value;
uint32_t mze_cd;
uin16_t mze_pad;
char mze_name[MZAP_NAME_LEN];
} mzap_ent_phys_t;
mze_value: 64位整数值。
mze_cd: 32比特冲突识别(“CD”):和一个条目关联,该条目的hash值和ZAP对象中另外一个条目相同。当一个条目插入ZAP对象中,被赋予了具有相同hash值的条目的没有使用的最小CD值。如果不存在hash冲突,则CD值为0。
mze_pad: 填充,保留以后使用。
mze_name: 以’\
肥ZAP有一个灵活的架构,用于存储大量的属性,这些属性可以有长的名称和复杂的值类型。本节首先描述肥ZAP对象的基本结构,然后对对象的每一个组成部分进行详细解析。
肥ZAP对象中的所有条目的安排是基于属性名的64位hash值的。该hash值用于在一个指针表中进行索引(如下图左边部分)。hash值中用于下标索引的位数(有时称之为前缀)取决于表中条目的数量。表中的条目数随时可能变化。当前的机制是,如果某个hash桶对应的冲突链表超过了一个块可以容纳的大小时,指针表将扩容。指针表的元素指向一个肥ZAP块链表,称为叶块,由zap_leaf_phys结构表示。每个叶块由一些chunk(zap_leaf_chunks)组成,每个属性存在一个或多个chunk中。下图显示基本的肥ZAP结构,其中的组件在随后详细描述:
图16 肥ZAP基本结构
肥ZAP对象的第一个块包含128KB大小的zap_phys_t结构,根据指针表的大小情况,指针表也可以被包含在这个结构结构中。如果指针表太大无法放进zap_phys_t,则在zap_phys_t的zap_table_phys域存放了指针表的位置信息。结构定义如下:
Struct zap_phys_t{
uint64_t zap_block_type
uint64_t zap_magic
struct zap_table_phys {
uint64_t zt_blk
uint64_t zt_numblks
uint64_t zt_shift
uint64_t zt_nextblk
uint64_t zt_blk_copied
} zap_ptrtbl;
uint64_t zap_freeblk
uint64_t zap_num_leafs
uint64_t zap_num_entries
uint64_t zap_salt
uint64_t zap_pad[8181]
uint64_t zap_leafs[8192]
}
zap_block_type:
表示ZAP块类型的64位整数,第一个块始终是ZBT_HEADER类型(参见表14)
zap_magic:
64位的ZAP魔数0x
zap_table_phys:
描述指针表的结构。
zt_blk:
指针表的第一个块的块id。该域仅用于指针表在zap_phys_t结构外的情况下。指针表在zap_phys_t中时,该域为0。
zt_numblks:
用于指针表的块数目。该域仅用于指针表在zap_phys_t结构外的情况下。指针表在zap_phys_t中时,该域为0。
zt_shift:
指针表hash值前缀的位数,如果指针表在zap_phys_t中,该值为13。
uint64_t zt_nextblk:
uint64_t zt_blks_copied:
这两个域在指针表尺寸变化时使用。
zap_freeblk:
第一个可用于分配新的zap_leaf的ZAP块
zap_num_leafs:
包含在这个ZAP对象中的zap_leaf_phys_t结构数
zap_salt:
salt值被放入hash函数,使得每个ZAP对象的hash函数各不相同。
zap_num_entries:
存放在ZAP对象中的属性数。
zap_leafs[8192]:
这个数组包含213个指针表条目,如果指针表小于213,可以存放在这里,否则,这个域不使用。
指针表是一个hash表,该hash表使用链表方法来处理冲突。每个hash桶包括一个64位整数指示层0块id,即冲突链表的第一个元素存储的位置。通过ZAP条目的属性名计算出一个64位hash值,使用这个hash值的高几位可以得到在指针表中对应的指针位置。Hash值中使用的这高几位称为前缀,其值由zt_shift指定。
指针表的元素指向zap_leaf_phys_t结构。指针表的hash冲突链表由zap_leaf_phys_t保存。这个结构包含一个头,一个hash表,以及一些chunk。
typedef struct zap_leaf_phys {
struct zap_leaf_header {
uint64_t lhr_block_type;
uint64_t lhr_next;
uint64_t lhr_prefix;
uint32_t lhr_magic;
uint16_t lhr_nfree;
uint16_t lhr_nentries;
uint16_t lhr_prefix_len;
uint16_t lh_freelist;
uint8_t lh_pad2[12];
} l_hdr; /* 2 24-byte chunks */
uint16_t l_hash[ZAP_LEAF_HASH_NUMENTRIES];
union zap_leaf_chunk {
struct zap_leaf_entry {
uint8_t le_type;
uint8_t le_int_size;
uint16_t le_next;
uint16_t le_name_chunk;
uint16_t le_name_length;
uint16_t le_value_chunk;
uint16_t le_value_length;
uint16_t le_cd;
uint8_t le_pad[2];
uint64_t le_hash;
} l_entry;
struct zap_leaf_array {
uint8_t la_type;
uint8_t la_array[ZAP_LEAF_ARRAY_BYTES];
uint16_t la_next;
} l_array;
struct zap_leaf_free {
uint8_t lf_type;
uint8_t lf_pad[ZAP_LEAF_ARRAY_BYTES];
uint16_t lf_next;
} l_free;
} l_chunk[ZAP_LEAF_NUMCHUNKS];
} zap_leaf_phys_t;
Header
ZAP叶的头存储在zap_leaf_header结构中。描述如下:
lhr_block_type: 始终为ZBT_LEAF(参见表14)
lhr_next: 块链表的下一个叶块id
lhr_prefix and lhr_prefix_len: hash值的高lhr_prefixlen位等于lhr_prefix的ZAP存储在这个叶(或叶链表)中。Lhr_prefixlen可以等于或者小于zt_shift(小于的情况下表示多个hash桶的冲突链表引用相同的叶)。
lhr_prefixlen can be equal to or less than zt_shift (the number of bits used to
index into the pointer table) in which case multiple pointer table buckets
reference the same leaf.
lhr_magic: 叶魔数 0x2AB1EAF (zap-leaf)
lhr_nfree: 这个叶中空闲chunk数。
lhr_nentries: 这个叶中存的ZAP条目
lhr_freelist:空闲chunk链表头,zap_leaf_chunk数组下标
Leaf Hash
下面的8KB是zap叶的hash表。Hash表中的条目指向zap_leaf_entry类型的chunk。ZAP属性条目名称的Hash值的lhr_prefix_len后面的12[c1] 用于这个hash表的索引。hash表中的每一个桶包含16位整数,作为zap_leaf_chunk数组的下标。
每一个叶包含一个chunk数组,有三种类型的chunk:zap_leaf_entry,zap_leaf_array,zap_leaf_free。每个属性由一些chunk组成,其中有一个zap_leaf_entry以及几个zap_leaf_array。下图展示这些chunk的安排。各种chunk类型的细节在后面描述。
图18 zap叶结构
Zap_leaf_entry:叶hash表指向这个类型的chunk。结构中包含了指向zap_leaf_array的指针,属性的名称和值存放在zap_leaf_array中。
le_type: ZAP_LEAF_ENTRY == 252
le_int_size: Size of integers in bytes for this entry.该条目的整数字节数。
le_next: hash冲突链表中的下一个zap_leaf_chunk。链表的最后条目的le_next为0xffff。
le_name_chunk:包含名称的前21个字符的zap_leaf_array类型的chunk标识。
le_name_length: 属性名称的长度,包括’\
le_value_chunk: 包含属性值的前21字节的第一个zap_leaf_array 类型chunk标识。
le_value_length: 属性值的长度(le_int_size)
le_cd: 冲突识别(CD)
le_hash: 属性名的64位hash值。
zap_leaf_array: zap_leaf_array类型的chunk存储ZAP属性的名称或者值。这些chunk能够组合起来提供一个对长名称和大值的支持。Zap_leaf_array由zap_leaf_entry中的指针指向。
la_type: ZAP_LEAF_ARRAY == 251
la_array: 21字节数组包含名称或者值。整数类型的值始终按大尾格式存储,不论处理器为何种字节序。
la_next: 16位整数的zap_leaf_chunk数组下标,指向该属性的下一个zap_leaf_array类型chunk,链表的最后元素该值为0xffff(CHAIN_END)。
zap_leaf_free:未使用的chunk放在空闲chunk链表中。空闲链表的头放在叶头中。
lf_type: ZAP_LEAF_FREE == 253
lf_next:16位整数,指向下一个空闲chunk。
[c1]这一段需要分析一下代码