nginx源码初读(7)--让烦恼从数据结构开始(ngx_queue)
2016-02-23 19:17
585 查看
ngx_queue_t是nginx中的双向链表,我们看看它的数据结构,太简单:
是不是很简单?学过数据结构的我们一看就懂了。唉?等一下!等一下!等一下~~我老公呢~等一下。。(乱入,什么东西- -)
等一下,我们发现它少了我们必须用到的数据存储啊,我们链表里的数据放哪儿去了。刚开始我也很疑惑,后来看到它用得地方瞬间懂了,不只是懂了,瞬间惊呆了!!
它是这么用的:
好了,知道它是怎么用的了,我们能放下好奇心好好看看代码了。
另外两个非宏的函数:
typedef struct ngx_queue_s ngx_queue_t; struct ngx_queue_s { ngx_queue_t *prev; ngx_queue_t *next; };
是不是很简单?学过数据结构的我们一看就懂了。唉?等一下!等一下!等一下~~我老公呢~等一下。。(乱入,什么东西- -)
等一下,我们发现它少了我们必须用到的数据存储啊,我们链表里的数据放哪儿去了。刚开始我也很疑惑,后来看到它用得地方瞬间懂了,不只是懂了,瞬间惊呆了!!
它是这么用的:
ngx_queue_t free; // 这是一个小哨兵,不存数据 ngx_queue_init(&free); // 初始化一下下 struct node{ int data; ngx_queue_t queue; // 把queue这个结构体乖乖的放在我们要创建链表的结构里 } node1; gx_queue_insert_head(&free, &node1.queue); // 对!就这么用,先把queue放任意结构体里,再连到free这个哨兵上,都可以做链表用啦,写这个的人真是绝了
好了,知道它是怎么用的了,我们能放下好奇心好好看看代码了。
#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 /* 插入到某节点的后面,跟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 /* 把节点从链表中删除的操作,在debug模式下还会重置指针为null */ #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分别为两个队列的指针,即头节点指针,该操作将n队列链接在h队列之后。 * 也有人表示完全看不懂~~ * 我其实也是看不懂的,画了好久,同学们可以自己画一下,我感觉n是一个单独的节点, * 然后把h远远连在前面(中间隔n个节点),把q连在后面(中间不隔节点),然后变成了一个环(大家画的时候要注意每个链表都是双向环形的)。 * 好吧,不好意思我也没弄对,应该是这样: * 大家动手画一画吧,这个因为源码也没注释~~ */ #define ngx_queue_add(h, n) \ (h)->prev->next = (n)->next; \ (n)->next->prev = (h)->prev; \ (h)->prev = (n)->prev; \ (h)->prev->next = h; /* h、n分别为两个队列的指针,即头节点指针,该操作将n队列链接在h队列之后。*/ #define ngx_queue_data(q, type, link) \ (type *) ((u_char *) q - offsetof(type, link)) /* 依靠内存位置的off_t来得到相应节点元素数据的内存地址,q为当前地址,type为相应结构体的类型,link为queue */
另外两个非宏的函数:
/* * 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; } } } /* 这个函数是用来获取列表最中间那个值的,使用了快慢指针的方法,mid移动1位,next就移动2位,返回mid */ void ngx_queue_sort(ngx_queue_t *queue, ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)); /* 使用了简单的插入排序算法,是一个稳定的排序 */
相关文章推荐
- 计蒜客:数据结构之哈希表
- nginx源码初读(6)--让烦恼从数据结构开始(ngx_list)
- linux源码中的核心数据结构
- nginx源码初读(5)--让烦恼从数据结构开始(ngx_array)
- 数据结构与算法之链表
- c语言实现最小生成树的普里姆算法(《数据结构》算法7.9)
- 数据结构-4
- 数据结构-3
- 数据结构-2
- 数据结构笔记-----线性表
- 《数据结构》进行曲(二)顺序表的链式表示(1)
- 数据结构-1
- 【数据结构】——较规范的链表操作方法
- 一个数据结构与算法可视化的网站
- 数据结构(11)--串的模式匹配算法之BF、KMP算法
- 数据结构与算法之数组
- 程序员面试题精选100题(39)-颠倒栈[数据结构]
- 程序员面试题精选100题(35)-两链表的第一个公共结点[数据结构]
- 程序员面试题精选100题(33)-在O(1)时间删除链表结点[数据结构]
- 程序员面试题精选100题(31)-从尾到头输出链表[数据结构]