1. 首页
  2. 技术知识

nginx之queue的具体使用

目录

    一、简介二、数据结构三、相关API

      3.1 初始化一个队列3.2 判断队列是否为空3.3 队头插入节点3.4 队尾插入节点3.5 从队列中移除某个节点3.6 将队列从某个节点拆分成两个队列3.7 将两个队列合并成一个队列3.8 队列排序3.9 获取队列中间节点3.10 获取原始数据

一、简介

​ nginx队列和linux内核中的链表有一样的结构,只有一个连接头(只有两个指针),任何包含这个结构的数据都可以连接在一起。有点像物联网,万物互联,只要能上网都可以连接。

​ nginx队列是带头节点的一个双向链表。

二、数据结构

  1. typedef struct ngx_queue_s  ngx_queue_t;
  2. struct ngx_queue_s {
  3.     ngx_queue_t  *prev;
  4.     ngx_queue_t  *next;
  5. };

复制代码


三、相关API


3.1 初始化一个队列

  1. #define ngx_queue_init(q)                                                     \
  2.     (q)->prev = q;                                                            \
  3.     (q)->next = q

复制代码

3.2 判断队列是否为空

只有一个头节点,则为空。有头节点的双向链表相比无头的双向链表,各种插入、删除等操作都更简单。

  1. #define ngx_queue_empty(h)                                                    \
  2.     (h == (h)->prev)

复制代码
3.3 队头插入节点

  1. #define ngx_queue_insert_head(h, x)                                           \
  2.     (x)->next = (h)->next;                                                    \
  3.     (x)->next->prev = x;                                                      \
  4.     (x)->prev = h;                                                            \
  5.     (h)->next = x

复制代码

头部插入节点后

3.4 队尾插入节点

  1. #define ngx_queue_insert_tail(h, x)                                           \
  2.     (x)->prev = (h)->prev;                                                    \
  3.     (x)->prev->next = x;                                                      \
  4.     (x)->next = h;                                                            \
  5.     (h)->prev = x

复制代码

尾部插入节点后

3.5 从队列中移除某个节点

  1. #define ngx_queue_remove(x)                                                   \
  2.     (x)->next->prev = (x)->prev;                                              \
  3.     (x)->prev->next = (x)->next

复制代码

移除x节点后

可以看到移除节点x后,x和队列还有一定的联系,所以对x的操作一定要小心,不然可能将整个队列损坏。
一般将x->prev,x->next都置空。

3.6 将队列从某个节点拆分成两个队列

  1. #define ngx_queue_split(h, q, n)                                              \
  2.     (n)->prev = (h)->prev;                                                    \
  3.     (n)->prev->next = n;                                                      \
  4.     (n)->next = q;                                                            \
  5.     (h)->prev = (q)->prev;                                                    \
  6.     (h)->prev->next = h;                                                      \
  7.     (q)->prev = n;

复制代码 将队列h从节点q拆分为h和n两个队列,并且q节点在n队列中。

拆分完后

3.7 将两个队列合并成一个队列

  1. #define ngx_queue_add(h, n)                                                   \
  2.     (h)->prev->next = (n)->next;                                              \
  3.     (n)->next->prev = (h)->prev;                                              \
  4.     (h)->prev = (n)->prev;                                                    \
  5.     (h)->prev->next = h;

复制代码

合并后

3.8 队列排序

  1. #define ngx_queue_head(h)                                                     \
  2.     (h)->next
  3. #define ngx_queue_last(h)                                                     \
  4.     (h)->prev
  5. #define ngx_queue_sentinel(h)                                                 \
  6.     (h)
  7. #define ngx_queue_next(q)                                                     \
  8.     (q)->next
  9. #define ngx_queue_prev(q)                                                     \
  10.     (q)->prev

复制代码

  1. #define ngx_queue_insert_after ngx_queue_insert_head

复制代码 使用标准的插入排序算法,通过传递的回调函数cmp进行比较,将整个队列排序。

  1. void
  2. ngx_queue_sort(ngx_queue_t *queue,
  3.     ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
  4. {
  5.     ngx_queue_t  *q, *prev, *next;
  6.     q = ngx_queue_head(queue);
  7.     if (q == ngx_queue_last(queue)) {
  8.         return;
  9.     }
  10.     for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {
  11.         prev = ngx_queue_prev(q);
  12.         next = ngx_queue_next(q);
  13.         ngx_queue_remove(q);
  14.         do {
  15.             if (cmp(prev, q) <= 0) {
  16.                 break;
  17.             }
  18.             prev = ngx_queue_prev(prev);
  19.         } while (prev != ngx_queue_sentinel(queue));
  20.         ngx_queue_insert_after(prev, q);
  21.     }
  22. }

复制代码
3.9 获取队列中间节点

通过快慢指针的方式获取中间节点。

  1. ngx_queue_t *
  2. ngx_queue_middle(ngx_queue_t *queue)
  3. {
  4.     ngx_queue_t  *middle, *next;
  5.     middle = ngx_queue_head(queue);
  6.     if (middle == ngx_queue_last(queue)) {
  7.         return middle;
  8.     }
  9.     next = ngx_queue_head(queue);
  10.     for ( ;; ) {
  11.         middle = ngx_queue_next(middle);
  12.         next = ngx_queue_next(next);
  13.         if (next == ngx_queue_last(queue)) {
  14.             return middle;
  15.         }
  16.         next = ngx_queue_next(next);
  17.         if (next == ngx_queue_last(queue)) {
  18.             return middle;
  19.         }
  20.     }
  21. }

复制代码
3.10 获取原始数据

  1. #define ngx_queue_data(q, type, link)                                         \
  2.     (type *) ((u_char *) q – offsetof(type, link))

复制代码 从队列中获取的节点类型都是ngx_queue_s,而不是实际的数据类型,需要将ngx_queue_s转换为原始的类型。其中offsetof是一个内置的表达式,计算某个成员变量在类型中的偏移量。

通过偏移计算到计算到原始类型地址,然X行类型强转获取原始类型。

比如如下调用

  1. q = ngx_queue_last(&cache->expire_queue);
  2. file = ngx_queue_data(q, ngx_cached_open_file_t, queue);

复制代码

q的地址减去offset获取到ngx_cached_open_file_t的地址,然后在强转为对应的类型。

到此这篇关于nginx之queue的具体使用的文章就介绍到这了,更多相关nginx queue内容请搜索软件技术网以前的文章或继续浏览下面的相关文章希望大家以后多多支持软件技术网!

原创文章,作者:starterknow,如若转载,请注明出处:https://www.starterknow.com/109118.html

联系我们