Nginx源码分析之ngx_queue_t

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值