队列介绍
队列是有序集合,先进先出FIFO,队列的概念很好理解,队列的应用也非常广泛如:循环队列、阻塞队列、并发队列、优先级队列等。Nginx数据结构是循环队列,prev前置节点环和next后置节点环。
Nginx链表还是非常有特色的,它是一种轻量级链表,这种链表不包含数据内容,只包含前后节点指针。在使用的时候需要作为带有节点变量的结构体(宿主)的成员变量存在(寄宿链表)。这个结构并非Nginx独有,比如Linux操作系统的链表
1.数据结构
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
2.操作
ngx_queue_t是轻量级链表操作非常好理解,就是维护节点关系,保证双链表结构。这些和关于双链表的基本操作是相同的,Nginx代码很清晰可自行查看。
2.1获取节点内容
根据寄宿链表获取节点内容的原理来获取ngx_queue_t的内容:
1.宿主结构体本身看做一个连续的内存区域
2.利用offsetof宏计算出寄宿链表成员变量相对于结构体起始位置的偏移量
3.寄宿链表的起始地址-寄宿链表成员变量相对于结构体起始位置的偏移量=结构体的起始位置
#define ngx_queue_data(q, type, link) \
(type *) ((u_char *) q - offsetof(type, link))
2.2获取中间节点
这是个很机巧的问题,双路遍历,移动速度相差一倍,速度快的指针到达末尾时,速度慢的就在中间位置。
ngx_queue_t *
ngx_queue_middle(ngx_queue_t *queue)
{
ngx_queue_t *middle, *next;
/*1.首尾相等,只有一个节点直接返回*/
middle = ngx_queue_head(queue);
if (middle == ngx_queue_last(queue)) {
return middle;
}
next = ngx_queue_head(queue);
/*2.双路遍历,middle遍历用于获取中间节点,next遍历用于寻找last节点。由于next遍历是middle遍历的2倍,所以当next遍历到last节点时,middle遍历到中间节点*/
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;
}
}
}
Nginx的队列吸取了Linux系统的很多特点,具有轻量级,速度快的特点。