nginx源码学习之ngx_queue:双向链表

缘由

今天开始看书深入理解Nginx。所以本博客所述的内容大多数都是此书的内容,算作是一种笔记。

ngx_queue

ngx_queue是一个双向链表,更多的内容请看深入理解Nginx一书的217页。
其功能和函数一共由两个文件来完成。

  • ngx_queue.c
  • ngx_queue.h

该双向链表完成了常见的功能(在下面的ngx_queue.h文件中可以看到,而在下面的ngx_queue.c可以看见一个插入排序和寻找链表中中间的元素)。

特殊之一

这里我想简述一下其特殊的地方:要使用该双向链表,只需要把结构体:ngx_queue_t,作为链表元素的结构体的一部分即可。如下所示一个元素,一个节点:

typedef struct {
	u_char *data;
	ngx_queue_t queueElement;//ngx_queue_t结构体的元素就是下一个元素的ngx_queue_t和上一个元素的ngx_queue_t
	int num;
}TestNode;

那么testNode就可以成为一个双向链表的一个元素。

特殊之二

由此引来另一个特殊的地方,就是如何通过ngx_queue_t queueElement 来得到相应的TestNode,进而得到TestNode的data和num呢?这使用到了__builtin_offsetof()  这是一个宏,在编译器中已经设计好了的函数,直接调 用即可,它可以得到结构体中的成员在该结构体内的偏移。利用__builtin_offsetof()  实现了双向链表的ngx_queue_data的宏,代码如下:

/*根据ngx_queue_t类型的指针减去link相对于type的偏移量
就可以得到TestNode结构体的位置,注意其返回的是type类型的指针
link是type的成员,而q是一个指向link的指针
注意我们现在要的是指向type的指针
*/
#define ngx_queue_data(q, type, link)                                         \
    (type *) ((u_char *) q - offsetof(type, link))

const ngx_queue_t *a;

TestNode* aNode=ngx_queue_data(a,TestNode,queueElement);
如此即可获得相应的TestNode的节点。

特殊之三

最后一个比较特殊的地方就是:必须要有一个ngx_queue_t作为表头,不包含内容,仅作为表头使用,书中称其为该双向链表容器本身。

其他内容,我觉得我再怎么讲也没有书中讲的完善,所以请大家还是看书吧。

例子

书上讲述了一个例子,我实际的完成了这个例子。我将双向链表的实现代码抽取出来,自己写main函数完成的。
例子非常简单,就是给上面那个结构体的元素排序,我也仔细看了现实这个双向链表的代码,在这份源码里真的学习到了很多东西,。特别是那个需找双向链表里中处于中间位置的那个元素,让我印象深刻。此外,一些宏的写法和设计,非常让我感叹,明白了如何在一个项目中真正的写出一个双向链表。

ngx_queue.h

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


/* Offset of member MEMBER in a struct of type TYPE. */
/*__builtin_offsetof()宏就是在编译器中已经设计好了的函数,直接调 用即可*/
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)




#ifndef _NGX_QUEUE_H_INCLUDED_
#define _NGX_QUEUE_H_INCLUDED_


typedef struct ngx_queue_s  ngx_queue_t;

struct ngx_queue_s {
    ngx_queue_t  *prev;
    ngx_queue_t  *next;
};


#define ngx_queue_init(q)                                                     \
    (q)->prev = q;                                                            \
    (q)->next = q


#define ngx_queue_empty(h)                                                    \
    (h == (h)->prev)

/*插入表头元素后面的第一个*/
#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


#define ngx_queue_insert_tail(h, x)                                           \
    (x)->prev = (h)->prev;                                                    \
    (x)->prev->next = x;                                                      \
    (x)->next = h;                                                            \
    (h)->prev = x


#define ngx_queue_head(h)                                                     \
    (h)->next


#define ngx_queue_last(h)                                                     \
    (h)->prev

/**
 * 返回双向链表的链表容器的指针
 */
#define ngx_queue_sentinel(h)                                                 \
    (h)


#define ngx_queue_next(q)                                                     \
    (q)->next


#define ngx_queue_prev(q)                                                     \
    (q)->prev


#if (NGX_DEBUG)

#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为表头的双链表,h为不包括q的表头,n为包括q的表头,q为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;

/*h是个表头,n也是个表头。把两个链表合并一下*/
#define ngx_queue_add(h, n)                                                   \
    (h)->prev->next = (n)->next;                                              \
    (n)->next->prev = (h)->prev;                                              \
    (h)->prev = (n)->prev;                                                    \
    (h)->prev->next = h;

/*根据ngx_queue_t类型的指针减去link相对于type的偏移量
就可以得到TestNode结构体的位置,注意其返回的是type类型的指针
link是type的成员,而q是一个指向link的指针
注意我们现在要的是指向type的指针
*/
#define ngx_queue_data(q, type, link)                                         \
    (type *) ((u_char *) q - offsetof(type, link))


#endif /* _NGX_QUEUE_H_INCLUDED_ */

ngx_queue.c

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */

#include "ngx_queue.h"
#define ngx_int_t int
#define u_char unsigned char

/*
 * find the middle queue element if the queue has odd number of elements
 * or the first element of the queue's second part otherwise
 * 否则会返回第二部分的第一个元素
 *
 * 哇塞!!看源码真的很爽啊。
 * 原来这样就可以找到一个双向链表内处于中间的那个元素啊
 * 两个指针,一个每次走一步,另一个每次走两步
 * 这样只需要遍历一边链表
 */

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;
        }
    }
}


/* 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) {/*在本列中,如果prev大于q的话,返回1*/
                break;
            }

            prev = ngx_queue_prev(prev);

        } while (prev != ngx_queue_sentinel(queue));/*prev是第一个比q小于元素*/

        ngx_queue_insert_after(prev, q);
    }
}

main函数文件

#include "ngx_queue.c"
#include "ngx_queue.h"
#include <stdio.h>
#define ngx_int_t int
#define u_char unsigned char


typedef struct {
	u_char *data;
	ngx_queue_t queueElement;
	int num;
}TestNode;

int compTestnode(const ngx_queue_t *a,const ngx_queue_t *b){
	TestNode* aNode=ngx_queue_data(a,TestNode,queueElement);
	TestNode* bNode=ngx_queue_data(b,TestNode,queueElement);
	return aNode->num > bNode->num;//根据num的大小来排序
}


int main() {
	ngx_queue_t queueContainer;
	ngx_queue_init(&queueContainer);
	int i=0;
	TestNode node[5];
	for(;i<5;i++){
		node[i].num=i;
	}

	ngx_queue_insert_tail(&queueContainer,&node[0].queueElement);//将元素插入到链表中
	ngx_queue_insert_head(&queueContainer,&node[1].queueElement);
	ngx_queue_insert_tail(&queueContainer,&node[2].queueElement);
	ngx_queue_insert_after(&queueContainer,&node[3].queueElement);
	ngx_queue_insert_tail(&queueContainer,&node[4].queueElement);


	ngx_queue_t* q;

	printf("排序前:");
	/*打印排序前的代码*/
	for(q=ngx_queue_head(&queueContainer);
		q!=ngx_queue_sentinel(&queueContainer);
		q=ngx_queue_next(q)
	){
		TestNode* eleNode=ngx_queue_data(q,TestNode,queueElement);
		printf("%d\t",eleNode->num);
	}
	printf("\n");
	ngx_queue_sort(&queueContainer,compTestnode);

	printf("排序后:");
	/*打印排序后的代码*/
	for(q=ngx_queue_head(&queueContainer);
		q!=ngx_queue_sentinel(&queueContainer);
		q=ngx_queue_next(q)
	){
		TestNode* eleNode=ngx_queue_data(q,TestNode,queueElement);
		printf("%d\t",eleNode->num);
	}
	printf("\n");
}

运行结果:

asd@asd-desktop:~/workspace/test/src$ ./a.out 
排序前:3        1	0	2	4	
排序后:0        1	2	3	4	
asd@asd-desktop:~/workspace/test/src$ 





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值