您的位置:首页 > 产品设计 > UI/UE

PVFS2源代码分析之公有组件src/common/quicklist

2010-07-15 01:17 459 查看
quicklist实现了一个双向循环链表。文件中也有提及,它是参照linux 2.4.3中的list.h设计的。这一设计主要有两个特点:

1. 使用了宿主
机制,提高了代码的通用性,详见下述数据结构说明;

2. 使用了大量的内联
函数和
定义,目的是简化部分代码书写,减小运行时的函数调用开销,详见下述功能函数说明。

数据结构

只有一个双向链表节点:

struct qlist_head {
    struct qlist_head *next, *prev;
};



不包含链表项是为了提高通用性,不然需要为每一种不同类型的链表项重复所有的功能函数,因为C语言没有模板或者装箱等通用机制(当然,可以考虑使用void指针来提高通用性,参见llist
)。



这种链表使用时需要宿主结构体
,才能产生实际作用,也就是说能通过链表节点,获得我们附加的数据。传统实现方式就是将这些附加信息直接写到如上节点结构体中,而通用实现方式下,则是写到用户自定义的宿主结构体中。例如:

struct entry {
    struct qlist_head qlist_node;
    // 其他所需数据,如一个整型
    int node_value;
};



使用时,调用如下宏定义:

#define qlist_entry(ptr, type, member) /
    ((type *)((char *)(ptr)-(unsigned long)((&((type *)0)->member))))



其中,ptr是链表节点的指针,type是宿主结构体的类型,member是链表节点在宿主结构体中的名称;(&((type *)0)->member)表示宿主结构地址为0时链表节点的地址,即链表节点在宿主结构中的偏移量。本例中获得宿主结构体地址的调用代码是:

some_entry = qlist_entry(qlist_head_p, struct entry, qlist_node);



进而可以获得该节点的附加信息some_entry->node_value。

功能函数

所有功能函数都作为内联函数写在.h文件中。原有C语言是没有内联机制的,也没有“inline”的保留字。在GCC中,添加了内联的私有扩展,为了防止和已有代码冲突,使用“__inline__”作为声明内联时的标识。

初始化

#define QLIST_HEAD_INIT(name) { &(name), &(name) }
#define QLIST_HEAD(name) /
    struct qlist_head name = QLIST_HEAD_INIT(name)



其中name是链表名,在QLIST_HEAD宏中声明了名为name的链表头,并调用QLIST_HEAD_INIT宏获得自身的地址分别赋值给链表头的next和prev指针,构成一个空链表。这一过程也可通过如下宏实现:

#define INIT_QLIST_HEAD(ptr) do { /
    (ptr)->next = (ptr); (ptr)->prev = (ptr); /
} while (0)



加上一层do-while“外套”是为了保证在多种上下文环境中,该宏替换不会引起歧义或编译错误,如

if (ptr != NULL) INIT_QLIST_HEAD(ptr); 
else QLIST_HEAD(qlist_name);



不使用do-while形式的宏定义就会发生问题。

【注意】该双向循环链表的链表头不包含在宿主中。

static __inline__ void __qlist_del(struct qlist_head * prev,
                                   struct qlist_head * next);
static __inline__ void qlist_del(struct qlist_head *entry);



【注意】删除操作只是将目标节点从链表中脱离,并不包含释放为该节点分配的内存空间等操作。

static __inline__ struct qlist_head* qlist_pop(struct qlist_head *head);



弹出链表第一项(链表头之后的物理节点)。

该函数与在链表头后插入新项的函数

static __inline__ void qlist_add(struct qlist_head *new, struct qlist_head *head)



配合,可以实现栈(stack)。

static __inline__ void qlist_splice(struct qlist_head *qlist, struct qlist_head *head)



合并两个链表,将前者(参数qlist)插入到后者的某个位置(参数head),即参数head既可以是链表头(相当于第二个链表接到第一个链表的后面),也可以是链表中的某个项(相当于第二个链表在此分开,插入第一个链表)。

【注意】调用此函数后,如果第一个链表qlist的链表头是占用堆空间的,那么需要手工将其释放。

#define qlist_for_each(pos, head) /
    for (pos = (head)->next; pos != (head); pos = pos->next)
#define qlist_for_each_safe(pos, scratch, head) /
    for (pos = (head)->next, scratch = pos->next; pos != (head);/
         pos = scratch, scratch = pos->next)



为方便书写遍历代码定义的宏。后面所谓“安全”遍历,意思是指针pos可以被释放,而不影响继续迭代,因为pos的下一项已经预存在了scratch中。

static inline struct qlist_head * qlist_find(
    struct qlist_head *list,
    int (*compare)(struct qlist_head *, void *),
    void *ptr)



查找链表中第一个符合条件的链表节点,满足的条件是(*compare) (pos, ptr)等于非零
,其中pos是当前遍历到的链表节点指针。



其余函数略
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: