最近开始学习libevent源码, 发现其存储各类event的队列用的是OpenBSD: queue.h的带有尾节点信息的队列实现(带有尾节点是显然的, 这样插入节点的时候不用从头开始遍历, 只要o(1)时间便可), 该实现用一系列宏实现的队列数据结构的抽象和范型, 发现其实现的确实很精妙, 因而就深刻进去学习其实现, 以备在之后的项目中使用。数据结构
tail queue涉及如下的数据结构:布局
#define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ }
看看libevent中是如何使用的:学习
TAILQ_HEAD (event_list, event); //声明了event_list这种数据结构 //扩展开就为 struct event_list { \ struct event *tqh_first; /* first element */ \ struct event **tqh_last; /* addr of last next element */ \ }
很明显咱们知道TAILQ_HEAD是用来声明event_list这种数据结构, 而这个链表的节点类型为struct event, tqh_first表示该链表的第一个节点, tqh_last表示最后一个节点的TAILQ_ENTRY字段中tqe_next值的地址(见下文)。指针
再看看TAILQ_ENTRY:code
struct event { 。。。 TAILQ_ENTRY (event) ev_next; TAILQ_ENTRY (event) ev_timeout_next; TAILQ_ENTRY (event) ev_add_next; 。。。 } //扩展开为 struct event { 。。。 struct { struct event *tqe_next; struct event **tqe_prev; } ev_next; struct { struct event *tqe_next; struct event **tqe_prev; } ev_timeout_next; struct { struct event *tqe_next; struct event **tqe_prev; } ev_add_next; 。。。 }
由此可知TAILQ_ENTRY是在链表的实际节点结构中储存链表先后继节点信息的数据类型。tqe_next表示下一个节点指针, tqe_prev表示前一个节点的TAILQ_ENTRY字段中tqe_next值的地址。队列
并且咱们发现:一、上面这两种结构中他们的内存分配是同样的 2 第二个字段tqe_prev、tqh_last都是个二级指针, 而不是前一个节点的指针或尾巴节点的指针(要是咱们实现一个待尾巴节点信息的链表, 估计咱们都会这样实现, 二级指针可让某些临界条件下的操做不用特殊处理, 《c与指针》的链表实现那一章有很好的说明), tqe_prev表示前一个节点的TAILQ_ENTRY字段中tqe_next值的地址, 实际上也是TAILQ_ENTRY字段的地址。内存
下面给出此链表的结构图:element
下面来看一下链表相关元素的访问:源码
#define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field))
#define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0)
#define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ } while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ } while (0)参考链表结构图, 很容易理解, 这里就很少作说明了。