1、数据结构
nginx中queue的数据结构非常简单,其中并不包含任何实际数据相关的结构,只有两个指针用于找到前后的元素。
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
2、基本操作
queue的操作也几乎都是通过宏定义来实现的,队列包含的具体操作如下:#define ngx_queue_init(q) //队列的初始化
#define ngx_queue_empty(h) //判断队列是否为空
#define ngx_queue_insert_head(h, x) //在头节点后插入一个节点
#define ngx_queue_insert_tail(h, x) //在尾部节点后插入一个节点
#define ngx_queue_head(h) //返回队列的头节点
#define ngx_queue_last(h) //返回队列的尾部节点
#define ngx_queue_next(q) //返回q节点后面的一个节点
#define ngx_queue_prev(q) //返回q节点的前一个节点
#define ngx_queue_remove(x) //将节点x从队列中移除
#define ngx_queue_split(h, q, n) //分割队列
#define ngx_queue_add(h, n) //链接队列
队列的这些操作与以前学过的链表的操作十分类似。除了上述操作外,链表还通过函数实现了获取中间节点以及队列排序。
获取中间节点:
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;
}
}
}
有上述代码可知,middle和next初始时都是指向队列的头节点,而middle每次移动一个节点,next每次移动两个节点,则当next遍历完整个队列时,middle则刚好遍历到了队列的一半,即指向队列的中间节点。
eg:middle=2时,next=2或3;middle=3时,next=4或5;即当next指向最后一个节点时,middle总是指向中间节点,即第(totalNodeNum/2 + 1)个节点。当节点总数为奇数时,则恰好返回中间节点,当节点总数为偶数时,则返回后半部分的第一个节点。
上图分别对应了偶数个节点和奇数个节点的情况
队列排序:
void
ngx_queue_sort(ngx_queue_t *queue,
ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
{
ngx_queue_t *q, *prev, *next;
q = ngx_queue_head(queue);
if (q == ngx_queue_last(queue)) {
return;
}
for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {
prev = ngx_queue_prev(q);
next = ngx_queue_next(q);
ngx_queue_remove(q);
do {
if (cmp(prev, q) <= 0) { //与前面已经排序好的节点相比较,找到合适的位置
break;
}
prev = ngx_queue_prev(prev);
} while (prev != ngx_queue_sentinel(queue));
ngx_queue_insert_after(prev, q); //将节点插入到该位置中
}
}
队列排序是依次遍历队列中的每一个节点,将其插入到前面已经排好序的队列中,这是一种稳定的简单插入排序。
3、由于单纯的队列只是简单的构成队列,而不涉及数据部分的,因此当我们使用队列时,需要自己设计数据结构,在其中嵌入数据部分和队列。
一个简单的示意图如下:
队列在数据结构中间,上面或下面都可能存放其它数据。通过queue将所有节点链接起来。
可通过如下的宏定义获取队列节点数据
#define ngx_queue_data(q, type, link) \
(type *) ((u_char *) q - offsetof(type, link))
其中q为指向queue的指针,type为自定义的嵌入了数据和队列的结构,link为queue结构在type结构中的名称
q减去queue在结构体中的偏移量即可得到指向type结构首地址的指针,然后通过(type *)类型转换即可得到type结构。如下图所示: