libevent源码分析之 tail queue
2014-04-30 19:25
405 查看
摘要: libevent源码分析 队列 尾节点
最近开始学习libevent源码, 发现其存储各种event的队列用的是OpenBSD: queue.h的带有尾节点信息的队列实现(带有尾节点是显然的, 这样插入节点的时候不用从头开始遍历, 只要o(1)时间即可), 该实现用一系列宏实现的队列数据结构的抽象和范型, 发现其实现的确实很精妙, 于是就深入进去学习其实现, 以备在以后的项目中使用。
tail queue涉及以下的数据结构:
看看libevent中是如何使用的:
很明显我们知道TAILQ_HEAD是用来声明event_list这种数据结构, 而这个链表的节点类型为struct event, tqh_first表示该链表的第一个节点, tqh_last表示最后一个节点的TAILQ_ENTRY字段中tqe_next值的地址(见下文)。
再看看TAILQ_ENTRY:
由此可知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字段的地址。
下面给出此链表的结构图:
下面来看一下链表相关元素的访问:
这段代码其它部分都很好理解, 唯独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字段的地址, 再通过此值就可以获取最后一个节点的指针。
以下是链表的基本操作:
最近开始学习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)参考链表结构图, 很容易理解, 这里就不多做说明了。
相关文章推荐
- Libevent源码分析-----event-config.h指明所在系统的环境
- Libevent源码分析 (1) hello-world
- Tor源码分析五 -- 客户端执行流程(libevent调度)
- libevent源码分析
- libevent源码分析(7)--2.1.8--信号事件处理机制分析
- Libevent源码分析-----event_signal_map
- Libevent源码分析-----bufferevent工作流程探究
- Libevent源码分析-----多线程、锁、条件变量(二)
- Libevent源码分析-----连接监听器evconnlistener
- libevent源码分析---让libevent支持多线程
- libevent高性能网络库源码分析——事件(event)及其接口(三)
- libevent源码分析--event_init()函数
- Libevent-2.1.8源码分析——日志消息
- Libevent源码分析-----evbuffer结构与基本操作
- Libevent源码分析-----开篇
- Libevent源码分析-----event-config.h指明所在系统的环境
- libevent源码分析
- libevent源码分析
- libevent源码分析(2)--初始化
- Libevent源码分析-event_base