Nginx 双向链表 ngx_queue_t

目录

一、基本概述

二、数据结构

三、接口描述与实现

1、相关宏接口

2、ngx_queue_middle

3、ngx_queue_sort

四、使用案例


整理自 nginx 1.9.2 源码 和 《深入理解 Nginx:模块开发与架构解析》

一、基本概述

双向链表的优势是可以快速进行数据插入、删除与合并操作,但其查询操作没有数组性能高。

nginx 下 ngx_queue_t 还具备如下优点:

(1)实现了排序功能;

(2)不负责节点元素的内存分配操作,只提供轻量级的节点管理功能;

(3)内存空间占用较小,每个节点元素只占用两个指针的内存损耗;

二、数据结构

typedef struct ngx_queue_s  ngx_queue_t;
struct ngx_queue_s {ngx_queue_t  *prev;ngx_queue_t  *next;
};

        Nginx 在设计 ngx_queue_t 时,由于容器与元素共用了 ngx_queue_t 结构体,为了避免 ngx_queue_t 结构体成员的意义混乱,Nginx封装了链表容器与元素的所有方法,这种情况非常少见,其他容器都需要直接使用成员变量来访问,唯有 ngx_queue_t 双向链表只能使用 API 接口进行数据访问。

三、接口描述与实现

        接口大多使用宏进行封装。

1、相关宏接口

// 初始化链表容器 q,并置为空
#define ngx_queue_init(q)                                                     \(q)->prev = q;                                                            \(q)->next = q// 检测链表容器是否为空,返回结果为 0 表示链表为空
#define ngx_queue_empty(h)                                                    \(h == (h)->prev)// 将 x 节点插到 h 节点的后面一位
#define ngx_queue_insert_head(h, x)                                           \(x)->next = (h)->next;                                                    \(x)->next->prev = x;                                                      \(x)->prev = h;                                                            \(h)->next = x// 将 x 节点插入 q 节点之后,此处可以直接复用 ngx_queue_insert_head
#define ngx_queue_insert_after   ngx_queue_insert_head// 将 x 插入 h 节点前面,链表首尾相连
#define ngx_queue_insert_tail(h, x)                                           \(x)->prev = (h)->prev;                                                    \(x)->prev->next = x;                                                      \(x)->next = h;                                                            \(h)->prev = x// 	返回链表容器 h 中的第一个元素节点 ngx_queue_t 指针
#define ngx_queue_head(h)                                                     \(h)->next//	返回链表容器 h 中最后一个元素节点 ngx_queue_t 指针
#define ngx_queue_last(h)                                                     \(h)->prev//	返回容器链表结构体的指针
#define ngx_queue_sentinel(h)                                                 \(h)//	返回 q 元素的下一个元素
#define ngx_queue_next(q)                                                     \(q)->next// 返回 q 元素的前一个元素
#define ngx_queue_prev(q)                                                     \(q)->prev// 从链表中移除 x 节点,注意因为是双向链表,所以只需要 x 节点作为参数即可
#define ngx_queue_remove(x)                                                   \(x)->next->prev = (x)->prev;                                              \(x)->prev->next = (x)->next/* h 为链表容器,q 为链表 h 中的一个元素,这个方法可以将链表 h 以元素 q 为界拆分为两个链表 h 和n,其中 h 由原链表的前半部分组成(不包含 q),而 n 由后半部分组成,q 为首元素,如果以前 n 有成员,则新的 n 为从 h 中拆分的部分加上 n 原有的数据 
*/
#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;// 将链表 n 合并到 h 链表的末尾
#define ngx_queue_add(h, n)                                                   \(h)->prev->next = (n)->next;                                              \(n)->next->prev = (h)->prev;                                              \(h)->prev = (n)->prev;                                                    \(h)->prev->next = h;/*返回 q 元素(ngx_queue_t类型)所属结构体的地址。q 为链表中某个节点指针 ngx_queue_t 类型;type 为链表元素的结构体类型名称(该结构体中必须包含 ngx_queue_t 类型的成员);1ink 是上面这个结构体中 ngx_queue_t 类型的成员名字;例如:typedef struct {u_char* str;ngx_queue_t qEle;int num;} TestNode;
*//* Offset of member MEMBER in a struct of type TYPE. */
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
#define ngx_queue_data(q, type, link)                                         \(type *) ((u_char *) q - offsetof(type, link))

2、ngx_queue_middle

        返回链表的中心元素,例如链表共有 N 个元素,则 ngx_queue_middle 将返回第(N/2 + 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);/*middle 指针每次循环探索一步、next 指针每次循环探索两步;当 next 抵达链表尾部时,middle 正好在链表中心位置。*/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;}}
}

3、ngx_queue_sort

void
ngx_queue_sort(ngx_queue_t *queue,ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
{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);// q 节点是当前需要排序的节点ngx_queue_remove(q); // 下面循环将决定把 q 节点插入到什么位置;// 从 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);}
}

四、使用案例

        定义一个简单的链表,并使用 ngx_queue_sort 方法对所有元素排序。在这个例子中,可以看到如何定义、初始化 ngx_queue_t 容器,如何定义任意类型的链表元素,如何遍历链表,如何自定义排序方法并执行排序。

// 链表元素结构体中必须包含 ngx_queue_t 类型的成员,它可以在任意位置
typedef struct 
{u_char* str;ngx_queue_t qEle;int num;
} TestNode;// 升序排序
ngx_int_t compTestNode(const ngx_queue_t* a, const ngx_queue_t* b)
{/*首先使用 ngx_queue_data 方法由 ngx_queue_t 变量获取元素结构体 TestNode 的地址 */TestNode* aNode = ngx_queue_data(a, TestNode, qEle);TestNode* bNode = ngx_queue_data(b, TestNode, qEle);//返回 num 成员的比较结果return aNode->num > bNode->num;
}// 定义双向链表容器 queueContainer,并将其初始化为空链表
// 注意,ngx_queue_t 结构体遍历必须使用 ngx_queue_init 初始化
ngx_queue_t queueContainer;
ngx_queue_init(&queueContainer);

    ngx_queue_t 双向链表是完全不负责分配内存的,每一个链表元素必须自己管理自己所占用的内存。因此,本例在进程栈中定义了 5 个 TestNode 结构体作为链表元素,并把它们的 num 成员初始化为 0,1,2,3,4, 如下所示。

int i = 0;
TestNode node[5];
for (; i <5; i++)
{node[i].num = i;
}

        下面把这 5 个 TestNode 结构体添加到 queueContainer 链表中,注意,这里同时使用了ngx_queue_insert_tailngx_queue_insert_headngx_queue_insert_after 3 个添加方法,链表中元素顺序以 num 标识应该为:3、1、0、2、4。

ngx_queue_insert_tail(&queueContainer, &node[0] qEle);
ngx_queue_insert_head(&queueContainer, &node[1].qEle);
ngx_queue_insert_tail(&queueContainer, &node[2].qEle);
// 在头节点之后插入
ngx_queue_insert_after(&queueContainer, &node[3].qEle);
ngx_queue_insert_tail(&queueContainer, &node[4].qEle);

        先排序,再从链表头部遍历到链表尾部。反向遍历可以使用 ngx_queue_last 和 ngx_queue_prev 实现。

// 升序排序
ngx_queue_sort(&queueContainer, compTestNode);
// 遍历链表
ngx_queue_t* q;
for (q = ngx_queue_head(&queueContainer); q != ngx_queue_sentinel(&queueContainer);q = ngx_queue_next(q))
{TestNode* eleNode = ngx_queue_data(q, TestNode, qEle);// 处理当前的链表元素 eleNode// ...
}

使用案例还可以参考:Nginx 源码学习-ngx的基本容器-ngx_queue-xueliangfei-ChinaUnix博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890227.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

亚信安全春节14天双倍假期通告

亚信安全14天双倍假期来袭 “网安福利王”再次实至名归 2024年 8773小时&#xff0c;31582680秒 亚信安全一直驰骋于云网安世界 奋战在“安全 数智化”的壮阔征途上 如今&#xff0c;新春的脚步渐近 长达14天的春节长假 能让我们暂且放下忙碌的工作 去除班味&#xff0c…

【时间之外】IT人求职和创业应知【71】-专利费

目录 2025 ICT产业趋势年会召开&#xff0c;2024年度ICT十大新闻重磅揭晓 海纳致远数字科技申请定制化插件驱动的数据分析专利 阿波罗智联取得语音数据的处理方法、装置、设备和存储介质专利 心勿贪&#xff0c;贵知足。 感谢所有打开这个页面的朋友。人生不如意&#xff0…

游戏《姆吉拉的假面》启动时提示“xinput1_3.dll丢失”怎么办?“xinput1_3.dll丢失”要怎么解决?

《姆吉拉的假面》报错&#xff1a;xinput1_3.dll丢失&#xff1f;这里有解决之道&#xff01; 在畅游《姆吉拉的假面》这款经典游戏时&#xff0c;你是否遇到过“xinput1_3.dll丢失”的报错信息&#xff1f;这个错误不仅会影响你的游戏体验&#xff0c;还可能让你陷入无法继续…

数据分析实战—鸢尾花数据分类

1.实战内容 (1) 加载鸢尾花数据集(iris.txt)并存到iris_df中,使用seaborn.lmplot寻找class&#xff08;种类&#xff09;项中的异常值&#xff0c;其他异常值也同时处理 。 import pandas as pd from sklearn.datasets import load_iris pd.set_option(display.max_columns, N…

hive注释comment中文乱码解决

问题描述 当使用以下命令查看表的元数据信息时出现中文乱码&#xff08;使用的是idea连接hive&#xff09; desc formatted test.t_archer; 解决 连接保存hive元数据的MySQL数据库&#xff0c;执行以下命令&#xff1a; use hive3; show tables;alter table hive3.COLUMNS_…

maven项目运行时NoSuchMethodError问题排查记录(依赖冲突解决)

控制台异常如下&#xff1a; Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: org.apache.commons.io.input.BoundedInputStream.builder()Lorg/apache/commons/io/input/BoundedInputStream$Builder;问题明显&#xff0c;根据NoSuchMethodError…

java 选择排序,涵盖工作原理、算法分析、实现细节、优缺点以及一些实际应用场景

选择排序的详细解析 更深入地探讨选择排序的各个方面&#xff0c;包括其工作原理、算法分析、实现细节、优缺点以及一些实际应用场景。 动画演示 1. 基本概念 选择排序是一种简单的比较排序算法。它的核心思想是将数组分为两个部分&#xff1a;已排序部分和未排序部分。每…

矩阵-向量乘法的行与列的解释(Row and Column Interpretations):中英双语

本文是学习这本书的笔记 网站是&#xff1a;https://web.stanford.edu/~boyd/vmls/ 矩阵-向量乘法的行与列的解释 矩阵-向量乘法&#xff08;Matrix-Vector Multiplication&#xff09;是线性代数中的基本操作&#xff0c;也是机器学习、数据科学和工程中常用的数学工具。本文…

基于海思soc的智能产品开发(巧用mcu芯片)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于开发车规级嵌入式软件的同学来说&#xff0c;socmcu这样的组合&#xff0c;他们并不陌生。但是传统的工业领域&#xff0c;比如发动机、医疗或…

带有 Elasticsearch 和 Langchain 的 Agentic RAG

作者&#xff1a;来自 Elastic Han Xiang Choong 讨论并实现 Elastic RAG 的代理流程&#xff0c;其中 LLM 选择调用 Elastic KB。 更多阅读&#xff1a;Elasticsearch&#xff1a;基于 Langchain 的 Elasticsearch Agent 对文档的搜索。 简介 代理是将 LLM 应用于实际用例的…

SmartX分享:NVMe-oF 介绍、SMTX ZBS 如何选择高性能场景解决方案与如何实现

目录 背景什么是 NVMe-oFZBS AccessiSCSI 与 iSERNMVe-oF 介绍NVMeNVMe-oFNVMe-oF 承载网络&#xff08;数据平面&#xff09; ZBS NVMe-oF 实现ZBS 接入策略ZBS 接入点分配策略性能测试 为什么要支持 RoCE引用 背景 前几篇文章&#xff0c;我们认识到了 SmartX 公司产品 SMTX…

【机器学习】机器学习的基本分类-强化学习(Reinforcement Learning, RL)

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是一种基于试错的方法&#xff0c;旨在通过智能体与环境的交互&#xff0c;学习能够最大化累积奖励的策略。以下是强化学习的详细介绍。 强化学习的核心概念 智能体&#xff08;Agent&#xff09; 执行动作并与环境…

MyBatis-Plus中isNull与SQL语法详解:处理空值的正确姿势

目录 前言1. 探讨2. 基本知识3. 总结 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 基本的Java知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#x…

Spring Boot 项目创建

创建一个新项目&#xff1a; 打开 Spring Initializr 网址&#xff1a;https://start.spring.io/ &#xff0c;然后创建一个新项目&#xff1a; springboot3.3.5_jdk17&#xff1a; Project&#xff08;Maven&#xff09;编程语言&#xff08;Java 17&#xff09;Spring Boo…

基于蓝牙通信的手机遥控智能灯(论文+源码)

1.系统设计 灯具作为人们日常生活的照明工具为人们生活提供光亮&#xff0c;本次基于蓝牙通信的手机遥控智能灯设计功能如下&#xff1a; &#xff08;1&#xff09;用户可以通过蓝牙通信模块的作用下&#xff0c;在手机端遥控切换智能灯不同的工作模式&#xff1b; &#x…

为什么光耦固态继电器(SSR)值得关注?

光耦固态继电器&#xff08;SSR&#xff09;作为现代电子控制系统中不可或缺的关键组件&#xff0c;正逐步取代传统机械继电器。通过利用光耦合技术&#xff0c;SSR不仅能够提供更高的可靠性&#xff0c;还能适应更加复杂和严苛的应用环境。在本文中&#xff0c;我们将深入探讨…

AI @国际象棋世界冠军赛: 从棋盘到科研创新之路

点击屏末 | 阅读原文 | 在小红书和 Google 谷歌回顾 WCC

leetcode二叉搜索树部分笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 二叉搜索树 1. 二叉搜索树的最小绝对差2. 二叉搜索树中第 K 小的元素3. 验证二叉搜索树 1. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中…

计算机工作流程

分析下面的计算机工作流程&#xff1a; 1.取数a至ACC&#xff1a;PC程序寄存器自增1&#xff0c;变成0&#xff08;可以理解为PC初始从-1开始自增&#xff09;&#xff1b;接着PC把当前指令的地址给到MAR&#xff08;地址寄存器&#xff09;&#xff1b;MAR拿到当前地址后&…

ffmpeg翻页转场动效的安装及使用

文章目录 前言一、背景二、选型分析2.1 ffmpeg自带的xfade滤镜2.2 ffmpeg使用GL Transition库2.3 xfade-easing项目 三、安装3.1、安装依赖&#xff08;[参考](https://trac.ffmpeg.org/wiki/CompilationGuide/macOS#InstallingdependencieswithHomebrew)&#xff09;3.2、获取…