本组件负责BMI的地址映射,该地址是一个64位整型值,通过它可以方便地定位到对应的引用(reference),每个引用代表一个BMI操作实例。
主要是reference-list.h头文件中定义的引用结构体(ref_st),如下。引用用于定位BMI操作实例,并存储相关信息。
struct ref_st { BMI_addr_t bmi_addr; /* the identifier passed out of the BMI layer */ char *id_string; /* the id string that represents this reference */ bmi_method_addr_p method_addr; /* address structure used by the method */ /* pointer to the appropriate method interface */ struct bmi_method_ops *interface; /* linked list entry */ struct qlist_head list_link; int ref_count; struct qhash_head hash_link; };
第3行成员bmi_addr是一个64位整型,是调用BMI接口时使用的地址,通过它可以定位到本引用。
第4行成员id_string是一个字符串型的ID,也可以用于定位本引用。
第5行和第7行记录了向方法(method)的映射,每个BMI操作都由具体的方法来实现,这两个成员就分别指向对应的方法地址和方法接口。其中的方法地址和上述的BMI地址类似,可用于定位某个方法实例。
第9行和第11行分别是quicklist的项和quickhash的项,前者链入引用表(reference list),后者链入一个quickhash的某行链表中,用于使用字符串ID定位BMI实例的引用。
第10行是引用计数,记录本BMI实例被使用的情况。
还有一个重要数据结构就是在reference-list.c文件中声明的全局变量str_table,如下。它是一个quickhash,存储了从字符串ID到引用的映射。
static struct qhash_table* str_table = NULL; #define STR_TABLE_SIZE 137
除了引用表的创建和清理,提供的接口函数可分为两组,一组用于通过BMI地址定位相应引用,一组用于通过字符串ID定位相应引用。
一、reference-list的创建和清理
函数1. ref_list_new
ref_list_p ref_list_new(void) { ref_list_p tmp_list = NULL; /* There is currently never more than one reference list in BMI. If we * ever have a need for more, then this hash table should be moved from * a static global to actually be part of the ref_list_p. */ assert(str_table == NULL); str_table = qhash_init( ref_list_compare_key_entry, quickhash_string_hash, STR_TABLE_SIZE); // ...... tmp_list = (ref_list_p) malloc(sizeof(struct qlist_head)); // ...... INIT_QLIST_HEAD(tmp_list); return (tmp_list); }
正如注释中所说,系统全局范围内只包括一个引用表。该创建函数初始化了全局变量str_table(第9~12行),而后返回了初始化的引用表。这表明,全局变量str_table和引用表是一一对应的,且存储着相同的引用集合,它们提供了不同的索引方式。
负责销毁引用表的函数是ref_list_cleanup,它逐一释放引用表中的各项,并销毁全局变量str_table,代码不再贴出。
二、用于BMI地址索引
通过BMI地址可以索引到对应引用,这些映射并不存储于上述数据结构中,而是存储在src/common/id-generator/id-generator.c中声明的quickhash全局变量s_id_gen_safe_table中。该数据与同文件中定义的若干功能函数构成一个公用的基础结构,用于存储通过64位整型索引的映射关系,映射到的值可以指向BMI引用,也可能指向状态机控制块等其他结构,我们在介绍状态机生命周期 时已有提及。
先从引用结构体ref_st的创建说起,因为ref_st在创建时即在s_id_gen_safe_table中进行了注册。
函数2. alloc_ref_st
ref_st_p alloc_ref_st(void) { int ssize = sizeof(struct ref_st); ref_st_p new_ref = NULL; new_ref = (ref_st_p) malloc(ssize); // ...... memset(new_ref, 0, ssize); id_gen_safe_register(&(new_ref->bmi_addr), new_ref); return (new_ref); }
第8行的函数id_gen_safe_register即定义在id-generator.c中,在该文件声明的全局变量s_id_gen_safe_table上注册了新建的引用new_ref,并通过第一个输出参数赋值给新建引用的成员bmi_addr。该函数我们进行过介绍,参见状态机生命周期 。
创建时进行注册之后,可通过BMI地址查找到相应的引用结构体。
函数3. ref_list_search_addr
ref_st_p ref_list_search_addr(ref_list_p rlp, BMI_addr_t my_addr) { return(id_gen_safe_lookup(my_addr)); }
调用id-generator.c中定义的函数id_gen_safe_lookup,返回指向目标引用结构体ref_st的指针。
【注意】第一个参数引用表指针rlp并未使用,因为查找目标并不是存储在引用表中,此处有一定的误导性。
三、用于字符串ID索引
这一组函数用于操作全局变量str_table及其对应的引用表。
函数4. ref_list_add
void ref_list_add(ref_list_p rlp, ref_st_p rsp) { if(rsp->id_string) { qhash_add(str_table, rsp->id_string, &rsp->hash_link); } qlist_add(&(rsp->list_link), rlp); }
可见,该函数有两个动作,一是将引用对应的字符串ID加入quickhash全局变量str_table,而引用的成员hash_link作为quickhash中的值项(第6行);二是将当前引用加入到引用表中(第8行),引用的成员list_link作为quicklist的项。对全局变量str_table和引用表的操作是成对出现的。
下面是通过字符串ID查找引用的函数。
函数5. ref_list_search_str
ref_st_p ref_list_search_str(ref_list_p rlp, const char *idstring) { struct qhash_head* tmp_link; tmp_link = qhash_search(str_table, (char*)idstring); // ...... return(qlist_entry(tmp_link, ref_st, hash_link)); }
第5行获得字符串ID在quickhash中映射到的项的地址,即指向引用结构体ref_st的成员hash_link的指针,而后通过该指针找到其宿主ref_st(第7行)。如不清楚宿主机制,请参见quicklist的介绍 。
另外,用于删除引用的函数ref_list_rem综合了引用所涉及的三个主要数据载体——s_id_gen_safe_table(全局声明于id-generator.c)、str_table和引用表,是帮助理解引用如何存储的典型示例。
函数6. ref_list_rem
ref_st_p ref_list_rem(ref_list_p rlp, BMI_addr_t my_addr) { ref_st_p tmp_entry; tmp_entry = id_gen_safe_lookup(my_addr); if(tmp_entry) { qlist_del(&tmp_entry->list_link); if(tmp_entry->id_string) { qhash_del(&tmp_entry->hash_link); } } return (tmp_entry); }
第5行相当于调用函数3 ,通过BMI地址从s_id_gen_safe_table中找到相应的引用(返回ref_st的指针),而后类似函数4 进行两方面工作,一是将其成员list_link从引用表中删除(第8行),二是将其成员hash_link从str_table中删除(第11行)。