LINUX下的TAILQ队列
来源:互联网 发布:淘宝江南商学院 编辑:程序博客网 时间:2024/05/22 17:39
队列,(英文queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。FreeBSD中的TAILQ把整个队列头抽象为一个单独的数据结构,我们先看看FreeBSD中的TAILQ相关宏,然后再举例子理解这些宏。 这里用最简单的一个结构体来理解TAILQ,这个结构体中有一个int型整数,还有两个分别指向前方和后方的指针。
1.描述前一个和下一个元素的结构体
458 #define TAILQ[5]_ENTRY(type) \459 struct { \460 struct type *tqe_next; /* next element */ \461 struct type **tqe_prev; /* address of previous next element */ \462 TRACEBUF \463 }
这是TAILQ对两个指向前后两个元素指针的抽象,抽象为TAILQ_ENTRY结构体:tqe_next是指向下一个元素的指针,tqe_prev是一个二级指针,指针变量的地址,是前一个元素的tqe_next的地址,解引用(*tqe_prev)之后就是本元素的内存地址;TRACEBUF是一个调试相关的宏,我们先不管它。举例: 我们声明一个结构体,这个结构体只有一个int型整数,还有前驱和后继指针。struct int_node{int num;TAILQ_ENTRY(int_node);};宏展开之后就变成:
struct int_node{int num;struct int_node *tqe_next; /* next element */ sturct int_node **tqe_prev; /* address of previous next element */};例如:
2.队列头
TAILQ把整个队列头单独抽象为一个结构体TAILQ_HEAD,如下:
445 /*446 * Tail queue declarations.447 */448 #define TAILQ_HEAD(name, type) \449 struct name { \450 struct type *tqh_first; /* first element */ \451 struct type **tqh_last; /* addr of last next element */ \452 TRACEBUF \453 }
这个宏实际上使用的时候,会展开成为一个结构体,tqh_first是一个一级指针,指向队列中的第一个元素;tqh_last是一个二级指针,它指向最后一个元素中的tqe_next(请参考上面的TAILQ_ENTRY),也就是最后一个元素的tqe_next的地址,指针的地址就是二级指针;TRACEBUF是一个用来调试的宏,不用管它。举例: 声明一个叫做queue_head的队列头:TAILQ_HEAD(my_int_struct, my_int) queue_head;宏展开之后就会变成(不管TRACEBUF宏):
struct int_head {struct int_node *tqh_first; /* first element */struct int_node **tqh_last; /* addr of last next element */} queue_head;如图:用下面的宏初始化这个队列头:
534 #define TAILQ_INIT(head) do { \535 TAILQ_FIRST((head)) = NULL; \536 (head)->tqh_last = &TAILQ_FIRST((head)); \537 QMD_TRACE_HEAD(head); \538 } while (0)变成:3.插入元素
插入元素用TAILQ_INSERT_TAIL宏,由于TAILQ中有一个tqh_last的二级指针,所以插入元素直接插到队尾,仅用O(1)时间。
578 #define TAILQ_INSERT_TAIL(head, elm, field) do { \579 QMD_TAILQ_CHECK_TAIL(head, field); \580 TAILQ_NEXT((elm), field) = NULL; \581 (elm)->field.tqe_prev = (head)->tqh_last; \582 *(head)->tqh_last = (elm); \583 (head)->tqh_last = &TAILQ_NEXT((elm), field); \584 QMD_TRACE_HEAD(head); \585 QMD_TRACE_ELEM(&(elm)->field); \586 } while (0)QMD_TAILQ_CHECK_TAIL,QMD_TRACE_HEAD,QMD_TRACE_ELEM这三个宏和调试信息相关和做一些必要的检查,我们可以先不管;这个宏就是在调整相关的指针指向。我们向一个空队列插入两个元素2来理解这个宏: 3.1 580行让新元素的tqe_next指向空,执行完第580行:3.2 581行让新元素的tqe_prev赋值为tqh_last,也就是指向队列头中的tqh_first的地址,执行完第581行:3.3 582行让二级指针tqh_last中的内容指向新元素,也就是tqh_first指向新元素,执行完第582行:3.4 583行,队列头的tqh_last赋值为新元素的tqe_next的地址(指针的地址,二级指针),执行完第583行:这就是插入2后的整个链表。
4.删除元素
删除元素用TAILQ_REMOVE宏
596 #define TAILQ_REMOVE(head, elm, field) do { \597 QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \598 QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \599 QMD_TAILQ_CHECK_NEXT(elm, field); \600 QMD_TAILQ_CHECK_PREV(elm, field); \601 if ((TAILQ_NEXT((elm), field)) != NULL) \602 TAILQ_NEXT((elm), field)->field.tqe_prev = \603 (elm)->field.tqe_prev; \604 else { \605 (head)->tqh_last = (elm)->field.tqe_prev; \606 QMD_TRACE_HEAD(head); \607 } \608 *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \609 TRASHIT(*oldnext); \610 TRASHIT(*oldprev); \611 QMD_TRACE_ELEM(&(elm)->field); \612 } while (0)QMD_SAVELINK,QMD_TAILQ_CHECK_NEXT,QMD_TAILQ_CHECK_PREV,TRASHIT,同样先不管这几个宏。我们从队列中删除一个元素来理解这个宏: 4.1 假设经过上节插入元素2之后,我们用TAILQ_INSERT_TAIL再插入一个元素1,没有删除之前的链表如下图:现在假设我们删除队列中的第一个元素2 4.2 602和603在调整当前元素的下一个元素的tqe_prev指针,执行完第602行和603行之后:4.3 608调整当前元素tqe_prev中的内容,执行完第608行之后:4.4 释放结点2的空间之后,最后的链表:5.队列中的第一个元素
512 #define TAILQ_FIRST(head) ((head)->tqh_first)
6.当前元素的下一个元素
591 #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)这个宏比较简单。
7.遍历链表中的每一个元素
用宏TAILQ_FOREACH
514 #define TAILQ_FOREACH(var, head, field) \515 for ((var) = TAILQ_FIRST((head)); \516 (var); \517 (var) = TAILQ_NEXT((var), field))这个宏就比较简单了,用临时变量var来遍历链表中的每一个元素。 这些宏就是几个操作TAILQ经常使用的宏,还有一些诸如TAILQ_INSERT_HEAD等宏,类似,请自行看代码,这里就不一一叙述了。这篇文章主要是理解相关的宏和插入删除过程,完整的应用例子请看下面的参考文献。
参考资料:
- 1.FreeBSD官方网站http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/anoncvs.html[6] 这里的代码示例是FreeBSD 7.0版本,上面这个链接教你如何获得FreeBSD的代码。
http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/cvs-tags.html[7] 上面这个链接是FreeBSD的tag信息。 - 2.graphviz画图软件http://www.graphviz.org/[8]
这里优美的图是用AT&T强大的开源画图工具graphviz生成的。为什么不用visio,dia,diagram designer等拖拉拽的画图工具?因为很难画成心中想要的那种图形,虽然还需要点学习时间,但是掌握之后画拓扑图就是传说中的所想即所得。关于如何用graphviz画出图示中的双向队列,请看我的另一篇博文:http://verynix.com/graphviz-tailq.html[9] - 3.其他TAILQ参考资料3.1 来自iteye的一篇很好的文章,举了一个应用TAILQ的例子http://bachmozart.iteye.com/blog/292836/[10] ,不过个人感觉里面的那个图对二级指针的描述还是不够好。
3.2 这里也提供了一个TAILQ应用的例子 http://blog.linuxphp.org/archives/1485/[11]
3.3 来自FreeBSDchina论坛的讲解 https://www.freebsdchina.org/forum/viewtopic.php?t=37913[12]
0 0
- LINUX下的TAILQ队列
- freeBSD TAILQ队列的理解
- Linux queue.h之TAILQ队列分析!
- Linux queue.h之TAILQ队列分析
- Linux queue.h之TAILQ队列分析
- 深入理解TAILQ队列
- 深入理解TAILQ队列
- 深入理解TAILQ队列
- 深入理解TAILQ队列
- 深入理解TAILQ队列
- 深入理解TAILQ队列
- 关于libevent与FreeBSD内核中TAILQ队列的理解
- tailq
- C语言之尾队列tailq
- C语言之尾队列tailq
- C语言之尾队列tailq
- C语言之尾队列tailq
- libevent中的双端队列TAILQ
- <java——常用对象API、其他对象>
- CSDN博客去图片水印
- hdu 2686 双线程DP
- Android编译系统(二)
- 在Myeclipse buildpath 加server lib (server runtime)
- LINUX下的TAILQ队列
- 查看主机DNS服务器
- AutoPostBack
- summary of big data science terms
- JavaScript概述
- 黑马程序员_线程小结
- 结构体在内存中是如何存储的
- TCP的拥塞控制
- SQL详解