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

libevent源码分析之 tail queue

2014-04-30 19:25 405 查看
摘要: libevent源码分析 队列 尾节点

最近开始学习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:

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值的地址。

而且我们发现:1、上面这两种结构中他们的内存分配是一样的 2 第二个字段tqe_prev、tqh_last都是个二级指针, 而不是前一个节点的指针或尾巴节点的指针(要是我们实现一个待尾巴节点信息的链表, 估计我们都会这样实现, 二级指针可以让某些临界条件下的操作不用特殊处理, 《c与指针》的链表实现那一章有很好的说明), tqe_prev表示前一个节点的TAILQ_ENTRY字段中tqe_next值的地址, 实际上也是TAILQ_ENTRY字段的地址。

下面给出此链表的结构图:



下面来看一下链表相关元素的访问:

#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))


这段代码其它部分都很好理解, 唯独TAILQ_LAST(head, headname), TAILQ_PREV(elm, headname, field) 有些复杂。就拿TAILQ_LAST(head, headname)分析, 观察链表结构图可知((head)->tqh_last))表示链表中最后一个节点的TAILQ_ENTRY字段中next结构的地址, 上文也讲过TAILQ_ENTRY字段中next结构的地址实际上也是TAILQ_ENTRY结构的地址, 由于(struct headname )与 TAILQ_ENTRY的内存布局是一样的, 所以(((struct headname *)((head)->tqh_last))得到的是倒数第二个节点TAILQ_ENTRY字段的地址, 再通过此值就可以获取最后一个节点的指针。

以下是链表的基本操作:

#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)
参考链表结构图, 很容易理解, 这里就不多做说明了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息