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

来源:互联网 发布:node中res.query 编辑:程序博客网 时间:2024/05/05 06:41

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

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

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

  • 数据结构

只有一个双向链表节点:

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

 

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

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

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

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

  • 功能函数

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

  1. 初始化

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

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

    不使用do-while形式的宏定义就会发生问题。
    【注意】该双向循环链表的链表头不包含在宿主中。

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

  3. 弹出链表第一项(链表头之后的物理节点)。
    该函数与在链表头后插入新项的函数

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

  4. 合并两个链表,将前者(参数qlist)插入到后者的某个位置(参数head),即参数head既可以是链表头(相当于第二个链表接到第一个链表的后面),也可以是链表中的某个项(相当于第二个链表在此分开,插入第一个链表)。
    【注意】调用此函数后,如果第一个链表qlist的链表头是占用堆空间的,那么需要手工将其释放。

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

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