Nginx-ngx_queue

来源:互联网 发布:级域名泛解析多久生效 编辑:程序博客网 时间:2024/04/24 04:04

    这一篇是要介绍一个常见的数据结构——队列,来看看nginx中是怎么实现的队列,先来看看队列的数据结构定义。

typedef struct ngx_queue_s  ngx_queue_t;struct ngx_queue_s {    ngx_queue_t  *prev;    ngx_queue_t  *next;};
nginx中实现的queue有两个特点:

1, 队列节点没有数据区,这是为了更好的抽像队列这个基础数据结构,ngx_queue_t这结构体是被封装到其他结构体中,这样来实现队列的功能。

2,实际上队列实现的是一个循环队列,其中有一个哨兵结点来连接首尾节点。

 从.h文件中可以看到关于队列的大量操作都是以宏定义的形式来实现的。各个宏的解释建下面代码中的注释。

//初始化队列哨兵结点,注意哨兵结点是不涉及到任何数据字段的,它只是一个首尾节点连接器define ngx_queue_init(q)                                                     \    (q)->prev = q;                                                            \    (q)->next = q//判断队列是否为空,如果哨兵节点和它的前一个节点指针一样,那么久没有数据节点#define ngx_queue_empty(h)                                                    \    (h == (h)->prev)//将节点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//将节点x加入到队列,并作为最后一个节点#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//删除当前节点x#define ngx_queue_remove(x)                                                   \    (x)->next->prev = (x)->prev;                                              \    (x)->prev->next = (x)->next#endif#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;//#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))
队列的基本操作都在上面了,下面看下两个高级点的操作,队列中间节点的获取或队列的排序。

1,获取队列中间节点: 其实这是一个我们在面试的时候经常被问到的一个题目,当然给出的解决办法也是大家熟知的快慢指针法。需要注意的是:如果队列的节点数为奇数,那么返回中间那个节点,如果为偶数,那么返回后一半的其实节点。

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;        }    }}                        
2,队列排序

voidngx_queue_sort(ngx_queue_t *queue,    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)){    //此函数里的cmp比较函数,需要自己另外提供。    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);        } while (prev != ngx_queue_sentinel(queue));        //将摘出来的节点插入到最终的位置        ngx_queue_insert_after(prev, q);    }}                                                                                    



原创粉丝点击