当前位置: 首页 > news >正文

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博客

相关文章:

Nginx 双向链表 ngx_queue_t

目录 一、基本概述 二、数据结构 三、接口描述与实现 1、相关宏接口 2、ngx_queue_middle 3、ngx_queue_sort 四、使用案例 整理自 nginx 1.9.2 源码 和 《深入理解 Nginx&#xff1a;模块开发与架构解析》 一、基本概述 双向链表的优势是可以快速进行数据插入、删除与…...

【vue】npm install 报错 python2 Error: not found: python2

如图所示&#xff0c;vue项目在下载依赖的时候报错找不到python2&#xff0c;有网友通过下载python2.7并配置环境变量解决了&#xff0c;这里有两个其他自测可用的方式&#xff0c;供各位作为参考。 报错的主要原因是因为【sass-loader】【node-sass】这两个依赖跟nodejs版本有…...

CS 144 check3: the TCP sender

Lecture Notes 略 Exercises 现在&#xff0c;在check3中&#xff0c;您将实现连接的另一边。 TCPSender是一种工具&#xff0c;它从出站字节流转换为将成为不可靠数据报的有效负载的段。 TCP sender的任务是确保receiver至少收到每个bytes一次。任务&#xff1a; 1、跟踪…...

Deepin/Linux clash TUN模式不起作用,因网关导致的问题的解决方案。

网关导致的问题的解决方案 查看路由 ip route寻找默认路由 默认路由应当为Mihomo default dev Mihomo scope link 如果不是&#xff0c;则 sudo ip route add default dev Mihomo在clash TUN开关状态发生变化时&#xff0c;Mihomo网卡会消失&#xff0c;所以提示找不到网卡…...

Tomato 靶机(通关攻略)

点击开启靶机 去kali终端输入 arp-scan -l //扫描靶机IP 扫出靶机IP192.168.131.171 第一步&#xff1a;信息收集 端口扫描 nmap -p- 192.168.131.171 敏感目录扫描 dirb http://192.168.131.171 总结&#xff1a; IP&#xff1a;192.168.168.131 开放端口&#xff1a;2…...

服务器被入侵登录不上怎么办?

在数字化时代&#xff0c;服务器作为数据存储与业务运行的核心载体&#xff0c;其安全性直接关系到企业的生死存亡。然而&#xff0c;随着网络攻击手段的不断升级&#xff0c;服务器被入侵的事件屡见不鲜&#xff0c;导致系统瘫痪、数据泄露等严重后果。当您发现自己的服务器被…...

达梦官方工具 SQLark数据迁移(oracle->达梦数据库)

应国产化需求需要,需将系统中涉及的各中间件替换成国产中间件,此文介绍了从Oracle迁移数据至达梦dm8的步骤,该文在windos环境下已验证测试过 1 SQLark介绍 SQLark是一款专为信创应用开发者设计的数据库开发和管理工具。它支持快速查询、创建和管理多种类型的数据库系统&#xf…...

redis数据类型:list

list 的相关命令配合使用的应用场景&#xff1a; 栈和队列&#xff1a;插入和弹出命令的配合&#xff0c;亦可实现栈和队列的功能 实现哪种数据结构&#xff0c;取决于插入和弹出命令的配合&#xff0c;如左插右出或右插左出&#xff1a;这两种种方式实现先进先出的数据结构&a…...

.NET周刊【12月第2期 2024-12-08】

国内文章 终于解决了.net在线客服系统总是被360误报的问题&#xff08;对软件进行数字签名&#xff09; https://www.cnblogs.com/sheng_chao/p/18581139 升讯威在线客服与营销系统由.net core和WPF开发&#xff0c;旨在开放、开源、共享。开发者为解决360与其他国产管家的误…...

C#—扩展方法

扩展方法 扩展方法是C#中一种特殊的静态方法&#xff0c;它定义在一个静态类中&#xff0c;但是可以像实例方法一样被调用&#xff0c;使得代码看起来更为直观和易于阅读。扩展方法允许你在不修改原始类的情况下&#xff0c;添加新的方法到现有的类型中。 有↓箭头的是扩展方…...

金碟中间件-AAS-V10.0安装

金蝶中间件AAS-V10.0 AAS-V10.0安装 1.解压AAS-v10.0安装包 unzip AAS-V10.zip2.更新license.xml cd /root/ApusicAS/aas# 这里要将license复制到该路径 [rootvdb1 aas]# ls bin docs jmods lib modules templates config domains …...

sql server 查询对象的修改时间

sql server 不能查询索引的最后修改时间&#xff0c;可以查询表&#xff0c;存储过程&#xff0c;函数&#xff0c;pk 的最后修改时间使用以下语句 select * from sys.all_objects ob order by ob.modify_date desc 但可以参考一下统计信息的最后修改时间&#xff0c;因为索…...

Qt之串口设计-线程实现(十二)

Qt开发 系列文章 - Serial-port&#xff08;十二&#xff09; 目录 前言 一、SerialPort 二、实现方式 1.创建类 2.相关功能函数 3.用户使用 4.效果演示 5.拓展应用-实时刷新 总结 前言 Qt作为一个跨平台的应用程序开发框架&#xff0c;在串口编程方面提供了方便易用…...

探索 Seaborn Palette 的奥秘:为数据可视化增色添彩

一、引言 在数据科学的世界里&#xff0c;视觉传达是不可或缺的一环。一个好的数据可视化不仅能传递信息&#xff0c;还能引发共鸣。Seaborn 是 Python 中一款广受欢迎的可视化库&#xff0c;而它的调色板&#xff08;palette&#xff09;功能&#xff0c;则为我们提供了调配绚…...

Linux创建普通用户和修改主机名

创建修改用户名和用户组 工作组相关命令 功能命令说明切换用户su username注销用户logout新建用户adduser username 创建用户并分配到用户组useradd -g test username 设置用户密码passwd username查看某一用户w username查看登录用户w查看登陆用户并显示IPwho查看登录历史…...

在 Spring Boot 3 中实现基于角色的访问控制

基于角色的访问控制 (RBAC) 是一种有价值的访问控制模型,可增强安全性、简化访问管理并提高效率。它在管理资源访问对安全和运营至关重要的复杂环境中尤其有益。 我们将做什么 我们有一个包含公共路由和受限路由的 Web API。受限路由需要数据库中用户的有效 JWT。 现在用户…...

二八(vue2-04)、scoped、data函数、父子通信、props校验、非父子通信(EventBus、provideinject)、v-model进阶

1. 组件的三大组成部分(结构/样式/逻辑) 1.1 scoped 样式冲突 App.vue <template><!-- template 只能有一个根元素 --><div id"app"><BaseOne></BaseOne><BaseTwo></BaseTwo></div> </template><script…...

配置PostgreSQL用于集成测试的步骤

在进行软件开发时&#xff0c;集成测试是确保各个组件能够协同工作的关键环节。PostgreSQL作为一种强大的开源数据库系统&#xff0c;常被用于集成测试中。下面将详细介绍如何在不同的环境中配置PostgreSQL以支持集成测试。 1. 选择并安装PostgreSQL 首先&#xff0c;你需要根…...

【ComfyUI + 铅笔素描画风】艺术家DaTou发布了的彩色铅笔素描风格生成(真实感超强)

发布时间&#xff1a;2024年12月09日 项目主页&#xff1a;https://hf-mirror.com/Datou1111/shou_xin 基础模型&#xff1a;flux.1-dev comfyui工作流下载&#xff1a;https://pan.baidu.com/s/1FrLQ4o8ldckKwhIrN1Pv7g?pwd1220 自己测试 官方效果 生成猫猫 shou_xin, a m…...

Unity-Editor扩展GUI基本实现一个可拖拉放的格子列表

短短几百行代码,好吧,又是“参考”了国外的月亮 操作,还真地挺自然的。。。。。。国外的实现有点小牛 拖拉,增加+ 一个Element 鼠标左键长按,可以出提示 鼠标右键,清除Element, 有点小bug,不是很自然地完全清除, using System.Collections; using System.Collecti…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...