0. 引言
上一篇文章中,我们已经了解了ZAP对象的两种类型(microzap和fatzap)是如何在磁盘上存放的。从本文开始,我们将进一步探索这两种ZAP在内存中的组织方式,它们对上层提供的接口,以及它们函数内部的实现。
本文主要介绍相对简单的microzap的实现方式。本文按照以下方式组织:
1. 首先详细描述microzap在内存中的组织方式和它在内存中定义的结构;
2. 介绍ZAP模块对上层提供的接口;
3. 介绍microzap中各个操作的具体实现。
1. microzap的内存结构
我们已经知道,microzap在磁盘上只占用一个数据块,它的属性条目(mzap_ent_phys_t)在磁盘上按照一个数组的形式保存。然而在内存中,我们为了实现属性的名字值对(name-value)的快速查找、快速插入和删除等操作,必须用一种更高效的方式进行组织。
ZFS采用了avl树(平衡二叉树)的方式组织内存中的microzap属性条目,关于avl树的介绍大家可以去查看其它相关资料,此处不再介绍。
下面的zap结构体即为ZAP对象在内存中的结构,它的定义如下:
typedef struct zap {
objset_t *zap_objset;
uint64_t zap_object;
struct dmu_buf *zap_dbuf;
krwlock_t zap_rwlock; /* rw_init, rw_enter, rw_exit, rw_destroy #nofrish# */
boolean_t zap_ismicro;
int zap_normflags;
uint64_t zap_salt;
union {
struct {
zap_phys_t *zap_phys;
kmutex_t zap_num_entries_mtx;
int zap_block_shift;
} zap_fat;
struct {
mzap_phys_t *zap_phys; /* memory copy of disk object content #nofrish# */
int16_t zap_num_entries;
int16_t zap_num_chunks; /* number of zap chunks #nofrish# */
int16_t zap_alloc_next; /* to allocate, start from this chunk, use cyclically #nofrish# */
avl_tree_t zap_avl;
} zap_micro;
} zap_u;
} zap_t;
该结构中各个字段的意义如下:
zap_object:表示该ZAP属于哪一个object set(对象集)中;
zap_object:表示该ZAP对象的对象号;
zap_dbuf:表示该ZAP对象在内存中的缓存;
zap_rwlock:读写锁;
zap_ismicro:表示该zap结构是否表示了一个microzap结构;
zap_normflags:该字段的意义还需要研究; ==toChange==
zap_salt:表示该ZAP对象加入其hash函数的参数,以使各个ZAP对象的hash函数不同;
下面是一个联合,此处我们仅介绍zap_micro结构体:
zap_phys是指向microzap在内存中的磁盘缓存的指针,它的类型是mzap_phys_t,定义如下(各字段的具体意义见上一篇文章):
typedef struct mzap_phys {
uint64_t mz_block_type; /* ZBT_MICRO */
uint64_t mz_salt;
uint64_t mz_normflags;
uint64_t mz_pad[5];
mzap_ent_phys_t mz_chunk[1];
/* actually variable size depending on block size */
} mzap_phys_t;
zap_num_entries:表示该ZAP对象中存储了多少个属性的名字值对;
zap_num_chunks:表示该ZAP对象上一共可以存储多少个属性的名字值对,对于一个数据块为128KB的microzap对象,共可以存储2047个名字值对;
zap_alloc_next:下一个空闲的子块(chunk,一个chunk可存放一个名字值对)的位置;注意,microzap的空闲子块并没有通过链表链接,故zap_alloc_next实际上表示插入一个名字值对时开始搜索空闲子块的位置,该值循环查找空闲子块,即 zap_alloc_next = (zap_alloc_next + 1)% zap_num_chunks;
zap_avl:表示zap中条目在内存中生成的avl树,它是avl_tree_t类型,定义如下:
struct avl_tree {
struct avl_node *avl_root; /* root node in tree */
int (*avl_compar)(const void *, const void *);
size_t avl_offset; /* offsetof(type, avl_link_t field) */
unsigned long avl_numnodes; /* number of nodes in the tree */
size_t avl_size; /* sizeof user type struct */
};
我们重点要关注的是avl_compar函数指针,表示该avl树中的元素进行比较操作的函数,对于microzap来说,它的比较函数定义如下:
static int
mze_compare(const void *arg1, const void *arg2)
{
const mzap_ent_t *mze1 = arg1;
const mzap_ent_t *mze2 = arg2;
if (mze1->mze_hash > mze2->mze_hash)
return (+1);
if (mze1->mze_hash < mze2->mze_hash)
return (-1);
/* If two entries have same hash, then compare the cd. #nofrish# */
if (mze1->mze_cd &g