ngx_queue_t是nginx中的双向链表,我们看看它的数据结构,太简单:
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
是不是很简单?学过数据结构的我们一看就懂了。唉?等一下!等一下!等一下~~我老公呢~等一下。。(乱入,什么东西- -)
等一下,我们发现它少了我们必须用到的数据存储啊,我们链表里的数据放哪儿去了。刚开始我也很疑惑,后来看到它用得地方瞬间懂了,不只是懂了,瞬间惊呆了!!
它是这么用的:
ngx_queue_t free; // 这是一个小哨兵,不存数据
ngx_queue_init(&free); // 初始化一下下
struct node{
int data;
ngx_queue_t queue; // 把queue这个结构体乖乖的放在我们要创建链表的结构里
} node1;
gx_queue_insert_head(&free, &node1.queue);
// 对!就这么用,先把queue放任意结构体里,再连到free这个哨兵上,都可以做链表用啦,写这个的人真是绝了
好了,知道它是怎么用的了,我们能放下好奇心好好看看代码了。
#define ngx_queue_init(q) \
(q)->prev = q; \
(q)->next = q
/* 初始化函数,哨兵的前后都是自己,也就是链表是空的,没有数据节点 */
#define ngx_queue_empty(h) \
(h == (h)->prev)
/* 判断链表是不是空的 */
#define ngx_queue_insert_head(h, x) \
(x)->next = (h)->next; \
(x)->next->prev = x; \
(x)->prev = h; \
(h)->next = x
/* 插入是直接插入头部的 */
#define ngx_queue_insert_after ngx_queue_insert_head
/* 插入到某节点的后面,跟insert_head是一样的 */
#define ngx_queue_insert_tail(h, x) \
(x)->prev = (h)->prev; \
(x)->prev->next = x; \
(x)->next = h; \
(h)->prev = x
/* 因为是双向循环链表,头尾相连,所以插入到尾部也很容易,就是头部的前一个元素 */
#define ngx_queue_head(h) \
(h)->next
#define ngx_queue_last(h) \
(h)->prev
/* 通过哨兵获得头尾元素 */
#define ngx_queue_sentinel(h) \
(h)
#define ngx_queue_next(q) \
(q)->next
#define ngx_queue_prev(q) \
(q)->prev
/* 返回自身或者前后的元素 */
#if (NGX_DEBUG)
#define ngx_queue_remove(x) \
(x)->next->prev = (x)->prev; \
(x)->prev->next = (x)->next; \
(x)->prev = NULL; \
(x)->next = NULL
#else
#define ngx_queue_remove(x) \
(x)->next->prev = (x)->prev; \
(x)->prev->next = (x)->next
#endif
/* 把节点从链表中删除的操作,在debug模式下还会重置指针为null */
#define ngx_queue_split(h, q, n) \
(n)->prev = (h)->prev; \
(n)->prev->next = n; \
(n)->next = q; \
(h)->prev = (q)->prev; \
(h)->prev->next = h; \
(q)->prev = n;
/* 注意一下!
* 有人是这么说的:其中,h、n分别为两个队列的指针,即头节点指针,该操作将n队列链接在h队列之后。
* 也有人表示完全看不懂~~
* 我其实也是看不懂的,画了好久,同学们可以自己画一下,我感觉n是一个单独的节点,
* 然后把h远远连在前面(中间隔n个节点),把q连在后面(中间不隔节点),然后变成了一个环(大家画的时候要注意每个链表都是双向环形的)。
* 好吧,不好意思我也没弄对,应该是这样:
* 大家动手画一画吧,这个因为源码也没注释~~
*/
#define ngx_queue_add(h, n) \
(h)->prev->next = (n)->next; \
(n)->next->prev = (h)->prev; \
(h)->prev = (n)->prev; \
(h)->prev->next = h;
/* h、n分别为两个队列的指针,即头节点指针,该操作将n队列链接在h队列之后。*/
#define ngx_queue_data(q, type, link) \
(type *) ((u_char *) q - offsetof(type, link))
/* 依靠内存位置的off_t来得到相应节点元素数据的内存地址,q为当前地址,type为相应结构体的类型,link为queue */
另外两个非宏的函数:
/*
* find the middle queue element if the queue has odd number of elements
* or the first element of the queue’s second part otherwise
*/
ngx_queue_t *
ngx_queue_middle(ngx_queue_t *queue)
{
ngx_queue_t *middle, *next;
middle = ngx_queue_head(queue);
if (middle == ngx_queue_last(queue)) {
return middle;
}
next = ngx_queue_head(queue);
for ( ;; ) {
middle = ngx_queue_next(middle);
next = ngx_queue_next(next);
// 偶数个节点,在此返回后半个队列的第一个节点
if (next == ngx_queue_last(queue)) {
return middle;
}
//奇数个节点,在此返回中间节点
next = ngx_queue_next(next);
if (next == ngx_queue_last(queue)) {
return middle;
}
}
}
/* 这个函数是用来获取列表最中间那个值的,使用了快慢指针的方法,mid移动1位,next就移动2位,返回mid */
void
ngx_queue_sort(ngx_queue_t *queue,
ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *));
/* 使用了简单的插入排序算法,是一个稳定的排序 */