分类: C/C++
2015-08-14 00:10:22
队列,又稱為佇列(英文queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。FreeBSD中的TAILQ把整个队列头抽象为一个单独的数据结构,我们先看看FreeBSD中的TAILQ相关宏,然后再举例子理解这些宏。 这里用最简单的一个结构体来理解TAILQ,这个结构体中有一个int型整数,还有两个分别指向前方和后方的指针。
458 #define TAILQ_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 */
};
例如: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)
变成: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行: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,没有删除之前的链表如下图:512 #define TAILQ_FIRST(head) ((head)->tqh_first)
591 #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)这个宏比较简单。
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等宏,类似,请自行看代码,这里就不一一叙述了。这篇文章主要是理解相关的宏和插入删除过程,完整的应用例子请看下面的参考文献。