缘由
今天开始看书深入理解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$