ngx_queue_t以双向链表的方式将数据组织起来。
源码位置:
nginx/src/core/ngx_queue.h
nginx/src/core/ngx_queue.c
(一)数据结构
ngx_queue_t是一个非常轻量级的双向链表,它不复则链表元素内存的分配,它只负责将已经分配好的节点串起来,因此它的结构体中只有两个指针。
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
(二)使用方法
nginx提供了很多操作双向链表的方法,大多用#define定义以及大部分都是链表操作。
/*q为链表容器结构体ngx_queue_t的指针,初始化链表容器*/
#define ngx_queue_init(q) \
(q)->prev = q; \
(q)->next = q
/*q为链表容器结构体ngx_queue_t的指针,判断链表容器是否为空*/
#define ngx_queue_empty(h) \
(h == (h)->prev)
/*h为链表容器结构体ngx_queue_t的指针*/
/*x为插入元素结构体中的指针,插入元素到头部*/
#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
/*h为链表容器结构体ngx_queue_t的指针*/
/*x为插入元素结构体中的指针,插入元素到尾部*/
#define ngx_queue_insert_tail(h, x) \
(x)->prev = (h)->prev; \
(x)->prev->next = x; \
(x)->next = h; \
(h)->prev = x
/*h为链表容器结构体ngx_queue_t的指针,返回第一个元素*/
#define ngx_queue_head(h) \
(h)->next
/*h为链表容器结构体ngx_queue_t的指针,返回最后一个元素*/
#define ngx_queue_last(h) \
(h)->prev
/*h为链表容器结构体ngx_queue_t的指针,返回返回链表容器的指针*/
#define ngx_queue_sentinel(h) \
(h)
#define ngx_queue_next(q) \
(q)->next
#define ngx_queue_prev(q) \
(q)->prev
#if (NGX_DEBUG)
/*从容器中移除x元素*/
#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
/*拆分链表,以q为分界,h为前半部分,n为后半部分*/
#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;
/*合并链表,将n链表添加到h链表的末尾*/
#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))
大部分都是链表操作,最后一个ngx_queue_data刚开始没看懂,后来看了示例代码就明白了,下文再说。
(三)元素排序
使用链表的插入排序,不适合大数据量,但是代码真的十分简洁,值得学习。
/* the stable insertion sort */
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);//往前搜索找到满足插入条件的prev
} while (prev != ngx_queue_sentinel(queue));
ngx_queue_insert_after(prev, q);//插入
}
}
(四)测试
由于ngx_queue_t只负责将数据结构组织起来,因此我们需要自定义的结构体中包含ngx_queue_t成员。写一个简单的测试程序,对ngx_queue_t进行排序。
4.1 ngx_queue_test.c
#include <stdio.h>
#include <string.h>
#include "ngx_config.h"
#include "nginx.h"
#include "ngx_conf_file.h"
#include "ngx_core.h"
#include "ngx_string.h"
#include "ngx_palloc.h"
#include "ngx_list.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_str_t name;
unsigned int score;
ngx_queue_t qEle;
}StuInfo;//结构体
/*比较函数*/
ngx_int_t
compStuIndo(const ngx_queue_t *a,const ngx_queue_t *b);
/*打印queue*/
void printQueue(ngx_queue_t*);
int main(int argc,char **argv)
{
ngx_queue_t queueContainer;
ngx_queue_init(&queueContainer);//初始化
ngx_pool_t *pool;
pool=ngx_create_pool(1024,NULL);//pool初始化
int i;
StuInfo students_[5];
//结构体初始化
for(i=0 ;i<5;++i)
{
char *buf=ngx_palloc(pool,30);
sprintf(buf,"ZhangXiao %d",i+1);
students_[i].name.len=strlen(buf);
students_[i].name.data=buf;
students_[i].score=i;
}
for(i=4;i>=0;--i)
{
//插入容器
ngx_queue_insert_tail(&queueContainer,&students_[i].qEle);
}
printf("------before sort----------\r\n");
printQueue(&queueContainer);
ngx_queue_sort(&queueContainer,compStuIndo);
printf("------after sort----------\r\n");
printQueue(&queueContainer);
return 0;
}
void printQueue(ngx_queue_t *pc)
{
ngx_queue_t *q;
for(q=ngx_queue_head(pc);
q!=ngx_queue_sentinel(pc);
q=ngx_queue_next(q))
{
StuInfo *eleNode = ngx_queue_data(q,StuInfo,qEle);
printf("Name: %s Score: %d\r\n",eleNode->name.data,eleNode->score);
}
}
ngx_int_t
compStuIndo(const ngx_queue_t *a,const ngx_queue_t *b)
{
StuInfo* aNode=ngx_queue_data(a,StuInfo,qEle);
StuInfo* bNode=ngx_queue_data(b,StuInfo,qEle);
return aNode->score > bNode->score;
}
4.2 Makefile
CC=gcc
INCLUDE=-I ${DIR}/src/core/ -I $(DIR)/objs/ -I $(DIR)/src/event -I $(DIR)/src/event/modules -I $(DIR)/src/os/unix
TARGETS=ngx_list_test ngx_queue_test
DIR=${HOME}/nginx/nginx-1.0.15
all:$(TARGETS)
NGX_OBJ =$(DIR)/objs/src/core/ngx_palloc.o $(DIR)/objs/src/core/ngx_string.o $(DIR)/objs/src/os/unix/ngx_alloc.o $(DIR)/objs/src/core/ngx_list.o $(DIR)/objs/src/core/ngx_queue.o
CFLAGS = -g -Wall -Wextra $(INCLUDE)
ngx_list_test:ngx_list_test.o
$(CC) $(CFLAGS) $^ $(NGX_OBJ) -o $@
ngx_queue_test:ngx_queue_test.o
$(CC) $(CFLAGS) $^ $(NGX_OBJ) -o $@
clean:
rm -f $(TARGETS) *.o
.PHONY: all clean
4.3 测试结果
(五) ngx_queue_data
这是个有意思的函数:
#define ngx_queue_data(q, type, link) \
(type *) ((u_char *) q - offsetof(type, link))
考虑我们的例子,结构体定义如下
typedef struct{
ngx_str_t name;
unsigned int score;
ngx_queue_t qEle;
}StuInfo;//结构体
通过offsetof计算得到ngx_queue_t成员在结构体中的偏移量,再用q的地址减去这个偏移量,就能得到StuIndo结构体的地址,也就是数据地址了。
(六)参考
1.深入理解Nginx
2.https://my.oschina.net/victorlovecode/blog/344421
2332

被折叠的 条评论
为什么被折叠?



