首页 > 其他 > 详细

Nginx 数据结构 ngx_queue_t

时间:2015-02-27 22:59:45      阅读:530      评论:0      收藏:0      [点我收藏+]
ngx_queue_t不分配内存,只是将已分配好的内存用双向链表连接。
消耗内存少,虽太适合超大规模数据的排序,但胜在简单使用。
作为C语言提供的通用双向链表,其设计思路值得参考。
在理解设计的时候可以将其想象成环形结构。


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

初始化容器
当容器一端(prev/next)不含有元素的时候,指向容器本身。

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

判断容器是否为空
如果容器与容器的prev相等,则容器不含任何元素

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

在容器h头部插入元素x:

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

在容器h尾部添加元素x

void ngx_queue_sort(ngx_queue_t *queue, ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *));

对容器进行排序,排序规则cmp要程序员自己实现。

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

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

分别获取容器的头部元素和尾部元素。
第一眼看上去比较奇怪,取得头部的为何是(h)->next而不是(h)->prev呢?
看完下边的几个示意图就明白了。


// 有一个ngx_queue_t容器
ngx_queue_t gather;

// 使用ngx_queue_init初始化
ngx_queue_init(&gather);

// 容器中为空的时候情况:
技术分享
技术分享

// 容器中含有1个元素的情况:
// 在h头部插入x, ngx_queue_insert_head

原先: 
h->prev = h;
h->next = h;

过程:
x->next = h; // h->next的值为h
h->prev = x; // x->next的值为h
x->prev = h;
h->next = x;

技术分享
技术分享

// 容器中含有2个元素的情况:
// 在h头部再插入y, ngx_queue_insert_head
原先:
h->prev = x;
x->prev = h;
h->next = x;
x->next = h;

#define ngx_queue_insert_head(h, y)   \
    (y)->next = (h)->next;            \
    (y)->next->prev = y;              \
    (y)->prev = h;                    \
    (h)->next = y

过程:
y->next = x; // (h)->next的值为x
x->prev = y; // (y)->next的值为x
y->prev = h;
h->next = y;

技术分享
技术分享

// 容器中含有3个元素的情况:
// 在h尾部添加元素z, ngx_queue_insert_tail
原先:

h->next = y;
y->next = x;
x->next = h;

x->prev = y;
y->prev = h;
h->prev = x;

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

过程:

z->prev = x;
x->next = z;
z->next = h;
h->prev = z;

技术分享
技术分享

通过以上示意图可以看到,容器的prev总指向容器的最后一个元素,next总指向容器的第一个元素。
仿佛该容器是一个圆环,头尾头尾头尾相互咬合。


使用:(示例来自于《深入理解Nginx: 模块开发与架构解析》)

ngx_queue_t可以作为另一个结构体的成员,然后该结构体对象通过该成员链接到链表上。
ngx_queue_t成员位置随意。

// 以下示例创建一个容器,然后添加5个元素,最后排序

struct TestNode
{
     char* str;
     ngx_queue_t qt;
     int num;
};

// 排序函数,通过num排序
ngx_int_t cmp(const ngx_queue_t* a, const ngx_queue_t* b)
{
     // 通过ngx_queue_data获取结构体TestNode对象的地址
     TestNode* aNode = ngx_queue_data(a, TestNode, qt);
     TestNode* bNode = ngx_queue_data(a, TestNode, qt);
     return aNode->num > bNode->num;
}

// 遍历链表
void trav(ngx_queue_t* q)
{
     ngx_queue_t* idx;
     for(idx = ngx_queue_head(q);
         idx != ngx_quque_sentinel(q);
         idx = ngx_quque_next(idx))
     {
          TextNode* node = ngx_queue_data(idx, TestNode, qt);
          printf("num: %d\n", node->num);
     }
}

TestNode q;
TestNode nodes[5];
for(int i = 0; i < 5; ++i)
     nodes[i].num = 5;

ngx_queue_insert_tail(&q, &nodes[0].qt);
ngx_queue_insert_head(&q, &nodes[1].qt);
ngx_queue_insert_tail(&q, &nodes[2].qt);
ngx_queue_insert_head(&q, &nodes[3].qt);
ngx_queue_insert_tail(&q, &nodes[4].qt);

// 遍历输出
trav(&q);

// 排序
ngx_queue_sort(&q, cmp);

// 遍历输出
trav(&q);

Nginx 数据结构 ngx_queue_t

原文:http://blog.csdn.net/alex_my/article/details/43973199

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!