1. 队列结构
nginx的队列是由具有头节点的双向循环链表实现的,每一个节点结构为ngx_queue_t,定义如下。
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s { //队列结构
ngx_queue_t *prev;
ngx_queue_t *next;
};
其中,sizeof(ngx_queue_t)=8。
从队列结构定义可以看出,nginx的队列结构里并没有其节点的数据内容。
2. 队列操作//初始化队列
ngx_queue_init(q)
//判断队列是否为空
ngx_queue_empty(h)
//在头节点之后插入新节点
ngx_queue_insert_head(h, x)
//在尾节点之后插入新节点
ngx_queue_insert_tail(h, x)
//删除节点x
ngx_queue_remove(x)
//分割队列
ngx_queue_split(h, q, n)
//链接队列
ngx_queue_add(h, n)
//获取队列的中间节点
ngx_queue_t *ngx_queue_middle(ngx_queue_t *queue)
//排序队列(稳定的插入排序)
void ngx_queue_sort(ngx_queue_t *queue,ngx_int_t (*cmp)(const ngx_queue_t*, const ngx_queue_t*))
其中,插入节点、取队列头、取队列尾等操作由宏实现,获取中间节点、排序等操作由函数实现。下面简单介绍。
2.1 在头节点之后插入
在头节点之后插入操作由宏ngx_queue_insert_head完成,如下。
#define ngx_queue_insert_head(h, x) \
(x)->next = (h)->next; \
(x)->next->prev = x; \
(x)->prev = h; \
(h)->next = x
画出该操作的逻辑图,如下。
图中虚线表示被修改/删除的指针,蓝色表示新修改/增加的指针。下同。
2.2 在尾节点之后插入在尾节点之后插入操作由宏ngx_queue_insert_tail完成,如下。
#define ngx_queue_insert_tail(h, x) \
(x)->prev = (h)->prev; \
(x)->prev->next = x; \
(x)->next = h; \
(h)->prev = x
该操作的逻辑图如下。
2.3 删除节点
在尾节点之后插入操作由宏ngx_queue_remove完成,如下。
#if (NGX_DEBUG)该操作的逻辑图如下。
2.4 分割队列
分割队列操作由宏ngx_queue_split完成,如下。
#define ngx_queue_split(h, q, n) \该宏有3个参数,h为队列头(即链表头指针),将该队列从q节点将队列(链表)分割为两个队列(链表),q之后的节点组成的新队列的头节点为n,图形演示如下。
2.5 链接队列
链接队列由宏ngx_queue_add完成,操作如下。
#define ngx_queue_add(h, n) \
其中,h、n分别为两个队列的指针,即头节点指针,该操作将n队列链接在h队列之后。演示图形如下。
宏ngx_queue_split和ngx_queue_add只在http模块locations相关操作中使用,在后续的讨论http模块locations相关操作时再详细叙述。
2.6 获取中间节点
中间节点,若队列有奇数个(除头节点外)节点,则返回中间的节点;若队列有偶数个节点,则返回后半个队列的第一个节点。操作如下。
/*
为什么该算法能够找到中间节点?
——注意代码中的next指针,其每次均会后移两个位置(节点),而middle指针每次后移一个位置(节点)。演示图形如下。
2.7 队列排序
队列排序采用的是稳定的简单插入排序方法,即从第一个节点开始遍历,依次将当前节点(q)插入前面已经排好序的队列(链表)中,下面程序中,前面已经排好序的队列的尾节点为prev。操作如下。
/* the stable insertion sort */该排序操作使用前面介绍的宏来完成其插入动作,只是一些简单的修改指针指向的操作,效率较高。关于该操作的例子,请参考后文的内容。
2.8 如何获取队列节点数据
由队列基本结构和以上操作可知,nginx的队列操作只对链表指针进行简单的修改指向操作,并不负责节点数据空间的分配。因此,用户在使用nginx队列时,要自己定义数据结构并分配空间,且在其中包含一个ngx_queue_t的指针或者对象,当需要获取队列节点数据时,使用ngx_queue_data宏,其定义如下。
#define ngx_queue_data(q, type, link) \由该宏定义可以看出,通过queue在节点中偏移,可以求出节点的起始地址。
3. 一个例子
/** * ngx_queue_t test */ #include <stdio.h> #include "ngx_config.h" #include "ngx_conf_file.h" #include "nginx.h" #include "ngx_core.h" #include "ngx_palloc.h" #include "ngx_queue.h" //2-dimensional point (x, y) queue structure typedef struct { int x; int y; } my_point_t; typedef struct { my_point_t point; ngx_queue_t queue; ngx_str_t app_name; ngx_int_t app_cnt; } my_point_queue_t; volatile ngx_cycle_t *ngx_cycle; #if 1 void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...) { (void)level; (void)log; (void) err; (void)fmt; } #endif #if 1 void dump_pool(ngx_pool_t* pool) { while (pool) { printf("pool = 0x%p\n", pool); printf(" .d\n"); printf(" .last = 0x%p\n", pool->d.last); printf(" .end = 0x%p\n", pool->d.end); printf(" .next = 0x%p\n", pool->d.next); printf(" .failed = %d\n", (int) pool->d.failed); printf(" .max = %d\n", (int)pool->max); printf(" .current = 0x%p\n", pool->current); printf(" .chain = 0x%p\n", pool->chain); printf(" .large = 0x%p\n", pool->large); printf(" .cleanup = 0x%p\n", pool->cleanup); printf(" .log = 0x%p\n", pool->log); printf("available pool memory = %d\n\n",(int) (pool->d.end - pool->d.last)); pool = pool->d.next; } } #endif void dump_queue_from_head(ngx_queue_t *que) { ngx_queue_t *q = ngx_queue_head(que); printf("(0x%p: (0x%p, 0x%p)) <==> \n", que, que->prev, que->next); for (; q != ngx_queue_sentinel(que); q = ngx_queue_next(q)) { my_point_queue_t *point = ngx_queue_data(q, my_point_queue_t, queue); printf("(%p: (%-2d, %-2d), app_name: %.*s, app_value: %d, %p: (%p, %p)) <==> \n", point, point->point.x, point->point.y, (int) point->app_name.len, point->app_name.data, (int) point->app_cnt, &point->queue, point->queue.prev, point->queue.next); } } void dump_queue_from_tail(ngx_queue_t *que) { ngx_queue_t *q = ngx_queue_last(que); printf("(0x%p: (0x%p, 0x%p)) <==> \n", que, que->prev, que->next); for (; q != ngx_queue_sentinel(que); q = ngx_queue_prev(q)) { my_point_queue_t *point = ngx_queue_data(q, my_point_queue_t, queue); printf("(0x%p: (%-2d, %-2d), 0x%p: (0x%p, 0x%p)) <==> \n", point, point->point.x, point->point.y, &point->queue, point->queue.prev, point->queue.next); } } //sort from small to big ngx_int_t my_point_cmp(const ngx_queue_t* lhs, const ngx_queue_t* rhs) { my_point_queue_t *pt1 = ngx_queue_data(lhs, my_point_queue_t, queue); my_point_queue_t *pt2 = ngx_queue_data(rhs, my_point_queue_t, queue); if (pt1->point.x < pt2->point.x) return 0; else if (pt1->point.x > pt2->point.x) return 1; else if (pt1->point.y < pt2->point.y) return 0; else if (pt1->point.y > pt2->point.y) return 1; return 1; } #define Max_Num 6 int main() { ngx_pool_t *pool; ngx_queue_t myque; my_point_queue_t *point; my_point_t points[Max_Num] = { {10, 1}, {20, 9}, {9, 9}, {90, 80}, {5, 3}, {50, 20} }; ngx_str_t app_names[] = { {4, (u_char*)"app1"}, {4, (u_char*)"app2"}, {8, (u_char*)"app_test"}, {4, (u_char*)"app4"}, {4,(u_char*) "app5"}, {5, (u_char*)"app_6"}, ngx_null_string }; int i; pool = ngx_create_pool(1024, NULL); dump_pool(pool); //myque = ngx_palloc(pool, sizeof(ngx_queue_t)); //alloc a queue head ngx_queue_init(&myque); //init the queue //insert some points into the queue for (i = 0; i < Max_Num; i++) { point = (my_point_queue_t *) ngx_palloc(pool, sizeof(my_point_queue_t)); point->point.x = points[i].x; point->point.y = points[i].y; point->app_name = app_names[i]; point->app_cnt = i; ngx_queue_init(&point->queue); //insert this point into the points queue ngx_queue_insert_head(&myque, &point->queue); } dump_queue_from_head(&myque); dump_pool(pool); ngx_destroy_pool(pool); return 0; }
CXX = gcc
CXXFLAGS += -g -Wall -Wextra
NGX_ROOT = /usr/nginx-1.4.6
TARGETS = ngx_queue_test
TARGETS_C_FILE = $(TARGETS).c
CLEANUP = rm -f $(TARGETS) *.o
all: $(TARGETS)
clean:
$(CLEANUP)
CORE_INCS = -I. \
-I$(NGX_ROOT)/src/core \
-I$(NGX_ROOT)/src/event \
-I$(NGX_ROOT)/src/event/modules \
-I$(NGX_ROOT)/src/os/unix \
-I$(NGX_ROOT)/objs \
-I$(NGX_ROOT)/src/proc \
-I$(NGX_ROOT)/pcre-8.31
NGX_PALLOC = $(NGX_ROOT)/objs/src/core/ngx_palloc.o
NGX_STRING = $(NGX_ROOT)/objs/src/core/ngx_string.o
NGX_ALLOC = $(NGX_ROOT)/objs/src/os/unix/ngx_alloc.o
NGX_QUEUE = $(NGX_ROOT)/objs/src/core/ngx_queue.o
$(TARGETS): $(TARGETS_C_FILE)
$(CXX) $(CXXFLAGS) $(CORE_INCS) $(NGX_PALLOC) $(NGX_STRING) $(NGX_ALLOC) $(NGX_QUEUE) $^ -o $@
3.3 运行结果
pool = 0x0x12dd010
.d
.last = 0x0x12dd060
.end = 0x0x12dd410
.next = 0x(nil)
.failed = 0
.max = 944
.current = 0x0x12dd010
.chain = 0x(nil)
.large = 0x(nil)
.cleanup = 0x(nil)
.log = 0x(nil)
available pool memory = 944
(0x0x7fff453fe050: (0x0x12dd068, 0x0x12dd158)) <==>
(0x12dd150: (50, 20), app_name: app_6, app_value: 5, 0x12dd158: (0x7fff453fe050, 0x12dd128)) <==>
(0x12dd120: (5 , 3 ), app_name: app5, app_value: 4, 0x12dd128: (0x12dd158, 0x12dd0f8)) <==>
(0x12dd0f0: (90, 80), app_name: app4, app_value: 3, 0x12dd0f8: (0x12dd128, 0x12dd0c8)) <==>
(0x12dd0c0: (9 , 9 ), app_name: app_test, app_value: 2, 0x12dd0c8: (0x12dd0f8, 0x12dd098)) <==>
(0x12dd090: (20, 9 ), app_name: app2, app_value: 1, 0x12dd098: (0x12dd0c8, 0x12dd068)) <==>
(0x12dd060: (10, 1 ), app_name: app1, app_value: 0, 0x12dd068: (0x12dd098, 0x7fff453fe050)) <==>
pool = 0x0x12dd010
.d
.last = 0x0x12dd180
.end = 0x0x12dd410
.next = 0x(nil)
.failed = 0
.max = 944
.current = 0x0x12dd010
.chain = 0x(nil)
.large = 0x(nil)
.cleanup = 0x(nil)
.log = 0x(nil)
available pool memory = 656
Reference
nginx源码分析—队列结构ngx_queue_t