Nginx 队列双向链表结构 ngx_quene_t

来源:互联网 发布:淘宝直通车优化软件 编辑:程序博客网 时间:2024/06/10 15:51

队列链表结构

        队列双向循环链表实现文件:文件:src/core/ngx_queue.h/.c。在 Nginx 的队列实现中,实质就是具有头节点的双向循环链表,这里的双向链表中的节点是没有数据区的,只有两个指向节点的指针。需注意的是队列链表的内存分配不是直接从内存池分配的,即没有进行内存池管理,而是需要我们自己管理内存,所有我们可以指定它在内存池管理或者直接在堆里面进行管理,最好使用内存池进行管理。节点结构定义如下:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 队列结构,其实质是具有有头节点的双向循环链表 */  
  2. typedef struct ngx_queue_s  ngx_queue_t;  
  3.   
  4. /* 队列中每个节点结构,只有两个指针,并没有数据区 */  
  5. struct ngx_queue_s {  
  6.     ngx_queue_t  *prev;  
  7.     ngx_queue_t  *next;  
  8. };  

队列链表操作

其基本操作如下:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* h 为链表结构体 ngx_queue_t 的指针;初始化双链表 */  
  2. ngx_queue_int(h)  
  3.   
  4. /* h 为链表容器结构体 ngx_queue_t 的指针; 判断链表是否为空 */  
  5. ngx_queue_empty(h)  
  6.   
  7. /* h 为链表容器结构体 ngx_queue_t 的指针,x 为插入元素结构体中 ngx_queue_t 成员的指针;将 x 插入到链表头部 */  
  8. ngx_queue_insert_head(h, x)  
  9.   
  10. /* h 为链表容器结构体 ngx_queue_t 的指针,x 为插入元素结构体中 ngx_queue_t 成员的指针。将 x 插入到链表尾部 */  
  11. ngx_queue_insert_tail(h, x)  
  12.   
  13. /* h 为链表容器结构体 ngx_queue_t 的指针。返回链表容器 h 中的第一个元素的 ngx_queue_t 结构体指针 */  
  14. ngx_queue_head(h)  
  15.   
  16. /* h 为链表容器结构体 ngx_queue_t 的指针。返回链表容器 h 中的最后一个元素的 ngx_queue_t 结构体指针 */  
  17. ngx_queue_last(h)  
  18.   
  19. /* h 为链表容器结构体 ngx_queue_t 的指针。返回链表结构体的指针 */  
  20. ngx_queue_sentinel(h)  
  21.   
  22. /* x 为链表容器结构体 ngx_queue_t 的指针。从容器中移除 x 元素 */  
  23. ngx_queue_remove(x)  
  24.   
  25. /* h 为链表容器结构体 ngx_queue_t 的指针。该函数用于拆分链表, 
  26.  * h 是链表容器,而 q 是链表 h 中的一个元素。 
  27.  * 将链表 h 以元素 q 为界拆分成两个链表 h 和 n 
  28.  */  
  29. ngx_queue_split(h, q, n)  
  30.   
  31. /* h 为链表容器结构体 ngx_queue_t 的指针, n为另一个链表容器结构体 ngx_queue_t 的指针 
  32.  * 合并链表,将 n 链表添加到 h 链表的末尾 
  33.  */  
  34. ngx_queue_add(h, n)  
  35.   
  36. /* h 为链表容器结构体 ngx_queue_t 的指针。返回链表中心元素,即第 N/2 + 1 个 */  
  37. ngx_queue_middle(h)  
  38.   
  39. /* h 为链表容器结构体 ngx_queue_t 的指针,cmpfunc 是比较回调函数。使用插入排序对链表进行排序 */  
  40. ngx_queue_sort(h, cmpfunc)  
  41.   
  42. /* q 为链表中某一个元素结构体的 ngx_queue_t 成员的指针。返回 q 元素的下一个元素。*/  
  43. ngx_queue_next(q)  
  44.   
  45. /* q 为链表中某一个元素结构体的 ngx_queue_t 成员的指针。返回 q 元素的上一个元素。*/  
  46. ngx_queue_prev(q)  
  47.   
  48. /* q 为链表中某一个元素结构体的 ngx_queue_t 成员的指针,type 是链表元素的结构体类型名称, 
  49.  * link 是上面这个结构体中 ngx_queue_t 类型的成员名字。返回 q 元素所属结构体的地址 
  50.  */  
  51. ngx_queue_data(q, type, link)  
  52.   
  53. /* q 为链表中某一个元素结构体的 ngx_queue_t 成员的指针,x 为插入元素结构体中 ngx_queue_t 成员的指针 */  
  54. ngx_queue_insert_after(q, x)  

下面是队列链表操作源码的实现:

初始化链表

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 初始化队列,即节点指针都指向自己,表示为空队列  */  
  2. #define ngx_queue_init(q)                                                     \  
  3.     (q)->prev = q;                                                            \  
  4.     (q)->next = q  
  5.   
  6. /* 判断队列是否为空 */  
  7. #define ngx_queue_empty(h)                                                    \  
  8.     (h == (h)->prev)  

获取指定的队列链表中的节点

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 获取队列头节点 */  
  2. #define ngx_queue_head(h)                                                     \  
  3.     (h)->next  
  4.   
  5. /* 获取队列尾节点 */  
  6. #define ngx_queue_last(h)                                                     \  
  7.     (h)->prev  
  8.   
  9.   
  10. #define ngx_queue_sentinel(h)                                                 \  
  11.     (h)  
  12.   
  13. /* 获取队列指定节点的下一个节点 */  
  14. #define ngx_queue_next(q)                                                     \  
  15.     (q)->next  
  16.   
  17. /* 获取队列指定节点的前一个节点 */  
  18. #define ngx_queue_prev(q)                                                     \  
  19.     (q)->prev  

插入节点

在头节点之后插入新节点:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 在队列头节点的下一节点插入新节点,其中h为头节点,x为新节点 */  
  2. #define ngx_queue_insert_head(h, x)                                           \  
  3.     (x)->next = (h)->next;                                                    \  
  4.     (x)->next->prev = x;                                                      \  
  5.     (x)->prev = h;                                                            \  
  6.     (h)->next = x  

        插入节点比较简单,只是修改指针的指向即可。下图是插入节点的过程:注意:虚线表示断开连接,实线表示原始连接,破折线表示重新连接,图中的数字与源码步骤相对应。


在尾节点之后插入节点

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 在队列尾节点之后插入新节点,其中h为尾节点,x为新节点 */  
  2. #define ngx_queue_insert_tail(h, x)                                           \  
  3.     (x)->prev = (h)->prev;                                                    \  
  4.     (x)->prev->next = x;                                                      \  
  5.     (x)->next = h;                                                            \  
  6.     (h)->prev = x  
下图是插入节点的过程:


删除节点

        删除指定的节点,删除节点只是修改相邻节点指针的指向,并没有实际将该节点的内存释放,内存释放必须由我们进行处理。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #if (NGX_DEBUG)  
  2.   
  3. #define ngx_queue_remove(x)                                                   \  
  4.     (x)->next->prev = (x)->prev;                                              \  
  5.     (x)->prev->next = (x)->next;                                              \  
  6.     (x)->prev = NULL;                                                         \  
  7.     (x)->next = NULL  
  8.   
  9. #else  
  10. /* 删除队列指定的节点 */  
  11. #define ngx_queue_remove(x)                                                   \  
  12.     (x)->next->prev = (x)->prev;                                              \  
  13.     (x)->prev->next = (x)->next  
  14.   
  15. #endif  
删除节点过程如下图所示:


拆分链表

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 拆分队列链表,使其称为两个独立的队列链表; 
  2.  * 其中h是原始队列的头节点,q是原始队列中的一个元素节点,n是新的节点, 
  3.  * 拆分后,原始队列以q为分界,头节点h到q之前的节点作为一个队列(不包括q节点), 
  4.  * 另一个队列是以n为头节点,以节点q及其之后的节点作为新的队列链表; 
  5.  */  
  6. #define ngx_queue_split(h, q, n)                                              \  
  7.     (n)->prev = (h)->prev;                                                    \  
  8.     (n)->prev->next = n;                                                      \  
  9.     (n)->next = q;                                                            \  
  10.     (h)->prev = (q)->prev;                                                    \  
  11.     (h)->prev->next = h;                                                      \  
  12.     (q)->prev = n;  
       该宏有 3 个参数,h 为队列头(即链表头指针),将该队列从 q 节点将队列(链表)拆分为两个队列(链表),q 之后的节点组成的新队列的头节点为 n 。链表拆分过程如下图所示:

合并链表

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 合并两个队列链表,把n队列链表连接到h队列链表的尾部 */  
  2. #define ngx_queue_add(h, n)                                                   \  
  3.     (h)->prev->next = (n)->next;                                              \  
  4.     (n)->next->prev = (h)->prev;                                              \  
  5.     (h)->prev = (n)->prev;                                                    \  
  6.     (h)->prev->next = h;                                                      \  
  7.     (n)->prev = (n)->next = n;/* 这是我个人增加的语句,若加上该语句,就不会出现头节点n会指向队列链表的节点 */  

其中,h、n分别为两个队列的指针,即头节点指针,该操作将n队列链接在h队列之后。具体操作如下图所示:



获取中间节点

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 返回队列链表中心元素 */  
  2. ngx_queue_t *  
  3. ngx_queue_middle(ngx_queue_t *queue)  
  4. {  
  5.     ngx_queue_t  *middle, *next;  
  6.   
  7.     /* 获取队列链表头节点 */  
  8.     middle = ngx_queue_head(queue);  
  9.   
  10.     /* 若队列链表的头节点就是尾节点,表示该队列链表只有一个元素 */  
  11.     if (middle == ngx_queue_last(queue)) {  
  12.         return middle;  
  13.     }  
  14.   
  15.     /* next作为临时指针,首先指向队列链表的头节点 */  
  16.     next = ngx_queue_head(queue);  
  17.   
  18.     for ( ;; ) {  
  19.         /* 若队列链表不止一个元素,则等价于middle = middle->next */  
  20.         middle = ngx_queue_next(middle);  
  21.   
  22.         next = ngx_queue_next(next);  
  23.   
  24.         /* 队列链表有偶数个元素 */  
  25.         if (next == ngx_queue_last(queue)) {  
  26.             return middle;  
  27.         }  
  28.   
  29.         next = ngx_queue_next(next);  
  30.   
  31.         /* 队列链表有奇数个元素 */  
  32.         if (next == ngx_queue_last(queue)) {  
  33.             return middle;  
  34.         }  
  35.     }  
  36. }  

链表排序

        队列链表排序采用的是稳定的简单插入排序方法,即从第一个节点开始遍历,依次将当前节点(q)插入前面已经排好序的队列(链表)中,下面程序中,前面已经排好序的队列的尾节点为prev。操作如下:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* the stable insertion sort */  
  2.   
  3. /* 队列链表排序 */  
  4. void  
  5. ngx_queue_sort(ngx_queue_t *queue,  
  6.     ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))  
  7. {  
  8.     ngx_queue_t  *q, *prev, *next;  
  9.   
  10.     q = ngx_queue_head(queue);  
  11.   
  12.     /* 若队列链表只有一个元素,则直接返回 */  
  13.     if (q == ngx_queue_last(queue)) {  
  14.         return;  
  15.     }  
  16.   
  17.     /* 遍历整个队列链表 */  
  18.     for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {  
  19.   
  20.         prev = ngx_queue_prev(q);  
  21.         next = ngx_queue_next(q);  
  22.   
  23.         /* 首先把元素节点q独立出来 */  
  24.         ngx_queue_remove(q);  
  25.   
  26.         /* 找到适合q插入的位置 */  
  27.         do {  
  28.             if (cmp(prev, q) <= 0) {  
  29.                 break;  
  30.             }  
  31.   
  32.             prev = ngx_queue_prev(prev);  
  33.   
  34.         } while (prev != ngx_queue_sentinel(queue));  
  35.   
  36.         /* 插入元素节点q */  
  37.         ngx_queue_insert_after(prev, q);  
  38.     }  
  39. }  

获取队列中节点数据地址

        由队列基本结构和以上操作可知,nginx 的队列操作只对链表指针进行简单的修改指向操作,并不负责节点数据空间的分配。因此,用户在使用nginx队列时,要自己定义数据结构并分配空间,且在其中包含一个 ngx_queue_t 的指针或者对象,当需要获取队列节点数据时,使用ngx_queue_data宏,其定义如下:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /* 返回q在所属结构类型的地址,type是链表元素的结构类型 */  
  2. #define ngx_queue_data(q, type, link)                                         \  
  3.     (type *) ((u_char *) q - offsetof(type, link))  
  4. /*  

测试程序:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include "ngx_queue.h"  
  3. #include "ngx_conf_file.h"  
  4. #include "ngx_config.h"  
  5. #include "ngx_palloc.h"  
  6. #include "nginx.h"  
  7. #include "ngx_core.h"  
  8.   
  9. #define MAX     10  
  10. typedef struct Score  
  11. {  
  12.     unsigned int score;  
  13.     ngx_queue_t Que;  
  14. }ngx_queue_score;  
  15. volatile ngx_cycle_t  *ngx_cycle;  
  16.   
  17. void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,    
  18.             const char *fmt, ...)  
  19. {  
  20. }  
  21.   
  22. ngx_int_t CMP(const ngx_queue_t *x, const ngx_queue_t *y)  
  23. {  
  24.     ngx_queue_score *xinfo = ngx_queue_data(x, ngx_queue_score, Que);  
  25.     ngx_queue_score *yinfo = ngx_queue_data(y, ngx_queue_score, Que);  
  26.   
  27.     return(xinfo->score > yinfo->score);  
  28. }  
  29.   
  30. void print_ngx_queue(ngx_queue_t *queue)  
  31. {  
  32.     ngx_queue_t *q = ngx_queue_head(queue);  
  33.   
  34.     printf("score: ");  
  35.     for( ; q != ngx_queue_sentinel(queue); q = ngx_queue_next(q))  
  36.     {  
  37.         ngx_queue_score *ptr = ngx_queue_data(q, ngx_queue_score, Que);  
  38.         if(ptr != NULL)  
  39.             printf(" %d\t", ptr->score);  
  40.     }  
  41.     printf("\n");  
  42. }  
  43.   
  44. int main()  
  45. {  
  46.     ngx_pool_t *pool;  
  47.     ngx_queue_t *queue;  
  48.     ngx_queue_score *Qscore;  
  49.   
  50.     pool = ngx_create_pool(1024, NULL);  
  51.   
  52.     queue = ngx_palloc(pool, sizeof(ngx_queue_t));  
  53.     ngx_queue_init(queue);  
  54.   
  55.     int i;  
  56.     for(i = 1; i < MAX; i++)  
  57.     {  
  58.         Qscore = (ngx_queue_score*)ngx_palloc(pool, sizeof(ngx_queue_score));  
  59.         Qscore->score = i;  
  60.         ngx_queue_init(&Qscore->Que);  
  61.   
  62.         if(i%2)  
  63.         {  
  64.             ngx_queue_insert_tail(queue, &Qscore->Que);  
  65.         }  
  66.         else  
  67.         {  
  68.             ngx_queue_insert_head(queue, &Qscore->Que);  
  69.         }  
  70.     }  
  71.   
  72.     printf("Before sort: ");  
  73.     print_ngx_queue(queue);  
  74.   
  75.     ngx_queue_sort(queue, CMP);  
  76.   
  77.     printf("After sort: ");  
  78.     print_ngx_queue(queue);  
  79.   
  80.     ngx_destroy_pool(pool);  
  81.     return 0;  
  82.   
  83. }  

输出结果:
[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. $./queue_test   
  2. Before sort: score:  8   6   4   2   1   3   5   7   9    
  3. After sort: score:  1    2   3   4   5   6   7   8   9    

总结

        在 Nginx 的队列链表中,其维护的是指向链表节点的指针,并没有实际的数据区,所有对实际数据的操作需要我们自行操作,队列链表实质是双向循环链表,其操作是双向链表的基本操作。


FROM:  http://blog.csdn.net/chenhanzhun/article/details/42453757
0 0
原创粉丝点击