采用ngx_quque_t来构建双向链表,可以将链表的链接操作相关的数据结构抽象出来,这样有利于进行链表操作函数的编写。其次,用ngx_queue_t结构串接起来的链表可以是不同类型的数据类型(只要这个数据类型包含ngx_quque_t这个数据结构)。打个不恰当的比喻,不管什么样的物品(数据类型),只要物品上有个孔(ngx_quque_t)我们就能用线(ngx_queue_t构成的链)将这些物品串起来。再者,对于链表而言,进行排序,移动元素等操作只需要修改ngx_queue_t中的相关指针即可,所以也称Nginx的双向链表结构为轻量级链表。
源码如下:
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
#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
#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
#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;
#define ngx_queue_add(h, n) \
(h)->prev->next = (n)->next; \
(n)->next->prev = (h)->prev; \
(h)->prev = (n)->prev; \
(h)->prev->next = h;
#define ngx_queue_data(q, type, link) \
(type *) ((u_char *) q - offsetof(type, link))
双向队列的各个操作都很简单,函数名即操作意图:
1. ngx_queue_init(q)初始化哨兵结点,令prev字段和next字段均指向其自身;2. ngx_queue_empty(q)检查哨兵结点的prev字段是否指向其自身,以判断队列是否为空;
3. ngx_queue_insert_head(h, x)在哨兵结点和第一个结点之间插入新结点x;
5. ngx_queue_insert_tail(h, x)在最后一个结点和哨兵结点之间插入新结点;
7. ngx_queue_last(h)获取最后一个结点;
8. ngx_queue_sentinel(h)获取哨兵结点(即参数h);
9. ngx_queue_next(q)获取下一个结点;
10. ngx_queue_prev(q)获取上一个结点;
11. ngx_queue_remove(x)将结点x从队列中移除;
12. ngx_queue_split(h, q, n)将h为哨兵结点的队列中q结点开始到队尾结点的整个链拆分、链接到空的n队列中,h队列中的剩余结点组成新队列;
13. ngx_queue_add(h, n)将n队列中的所有结点按顺序链接到h队列末尾,n队列清空;
15. ngx_queue_sort(queue, cmd)使用插入排序算法对queue队列进行排序,完成后在next方向上为升序,prev方向为降序。
16.ngx_queue_data
这里举个列子来说明这个操作的用法。
typedef struct
{
ngx_int_t num;
ngx_str_t str;
ngx_queue_t queue;
}TestNode;
如果我们有一个ngx_queue_t的指针q指向testNode.queue,现在我们不知到testNode的地址,只知道queue,如果我们想访问testNode里
面的成员num,我们必须知道testNode的地址,这样才能访问其num成员。怎样知道testNode的地址呢?这时候ngx_queue_data就闪亮登
场了。我们可以用一下语句来取得testNode的地址:
TestNode* testnode = ngx_queue_data(q, TestNode, queue);
这样我们就可以访问num了。
#include <stdio.h>
#include <stdlib.h>
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_conf_file.h>
#include <nginx.h>
#include <ngx_queue.h>
//
//
//这两个东东必须写,不为有编译错误
volatile ngx_cycle_t *ngx_cycle;
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
const char *fmt, ...)
{
}
//
//
typedef struct
{
ngx_int_t num;
ngx_str_t str;
ngx_queue_t queue;
}TestNode;
ngx_int_t compare_node(const ngx_queue_t *left, const ngx_queue_t *right)
{
TestNode* left_node = ngx_queue_data(left, TestNode, queue);
TestNode* right_node = ngx_queue_data(right, TestNode, queue);
return left_node->num > right_node->num;
}
int main()
{
ngx_queue_t QueHead;
ngx_queue_init(&QueHead);
TestNode Node[10];
ngx_int_t i;
for (i=0; i<10; ++i)
{
Node[i].num = rand()%100;
}
ngx_queue_insert_head(&QueHead, &Node[0].queue);
ngx_queue_insert_tail(&QueHead, &Node[1].queue);
ngx_queue_insert_after(&QueHead, &Node[2].queue);
ngx_queue_insert_head(&QueHead, &Node[4].queue);
ngx_queue_insert_tail(&QueHead, &Node[3].queue);
ngx_queue_insert_head(&QueHead, &Node[5].queue);
ngx_queue_insert_tail(&QueHead, &Node[6].queue);
ngx_queue_insert_after(&QueHead, &Node[7].queue);
ngx_queue_insert_head(&QueHead, &Node[8].queue);
ngx_queue_insert_tail(&QueHead, &Node[9].queue);
ngx_queue_t *q;
for (q = ngx_queue_head(&QueHead); q != ngx_queue_sentinel(&QueHead); q = ngx_queue_next(q))
{
TestNode* Node = ngx_queue_data(q, TestNode, queue);
printf("Num=%d\n", Node->num);
}
ngx_queue_sort(&QueHead, compare_node);
printf("\n");
for (q = ngx_queue_head(&QueHead); q != ngx_queue_sentinel(&QueHead); q = ngx_queue_next(q))
{
TestNode* Node = ngx_queue_data(q, TestNode, queue);
printf("Num=%d\n", Node->num);
}
return 0;
}