nginx源码分析--双端列表

news2025/7/20 20:57:05

1.基本数据结构

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

结构成员:

ngx_queue_t *prev;前驱指针

ngx_queue_t *next;后继指针

2.操作函数--头结点

2.1基本函数

define ngx_queue_init(q)                                                     \
    (q)->prev = q;                                                            \
    (q)->next = q


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

#define ngx_queue_sentinel(h)                                                 \
    (h)



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


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

1.初始化头结点 将两个指针指向自身

2.检测前驱结点

3返回头结点(哨兵:)

4.头结点和尾结点

 

2.2添加和删除

#define ngx_queue_insert_head(h, x)  

#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)

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

    删除

#define ngx_queue_remove(x)  
    (x)->next->prev = (x)->prev;                                              \
    (x)->prev->next = (x)->next;                                              \
    (x)->prev = NULL;                                                         \
    (x)->next = NULL

3.合并和分离

#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;


#define ngx_queue_add(h, n)                                                   \
    (h)->prev->next = (n)->next;                                              \
    (n)->next->prev = (h)->prev;                                              \
    (h)->prev = (n)->prev;                                                    \
    (h)->prev->next = h;

1.分裂队列 h是头 q是分裂节点的界限 n为分裂完的新节点

在这里插入图片描述

 2.合并队列 n本身的节点会抛弃

在这里插入图片描述

 3.操作函数--数据节点

数据节点本质上与头节点并无区别,所以很多操作代码是相同的数据节点本质上与头节点并无区别,所以很多操作代码是相同的
添加和删除
#define ngx_queue_remove(x)                                                   \
    (x)->next->prev = (x)->prev;                                              \
    (x)->prev->next = (x)->next

#define ngx_queue_insert_after   ngx_queue_insert_head

注意:

函数宏ngx_queue_remove()可以“删除” 当前节点,实际上它只是调整了节点的指针,
把节点从队列里摘除,并没有真正从内存里删除数据:
寻找:
#define ngx_queue_next(q)                                                     \
    (q)->next


#define ngx_queue_prev(q)                                                     \
    (q)->prev

ngx_queue_t *
ngx_queue_middle(ngx_queue_t *queue)
gx_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);

    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;
        }
    }
}
void 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);

        ngx_queue_remove(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);
    }
}

全是暴力算法 效率比较低 不做过多解释

4 test

引出一个知识:

双端队列是在两端都可以插入或删除元素的数据结构,在Nginx里它被实现为双向循环链表ngx_queue_t,类似标准容器std : : list。

借用Boost程序库的概念,前面的ngx_array_t和ngx_list_t属于非侵入式容器,元素无须改动即可加入容器,而 ngx_queue_t 则不同,它是侵入式容器,必须把 ngx_queue_t作为元素的一个成员,然后才能放入队列,与boost.intrusive库很接近。

struct Xinfo
{
int x=0;
ngx_queue_t queue;
}


例子

#include <stdio.h>                                                                       #include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "ngx_queue.h"

//数据定义 节点必须有ngx_queue_t
typedef struct student
{
    int id; 
    int age;
    int weight;
    ngx_queue_t qnode;
}student;
//排序比较函数
ngx_int_t stu_cmp(const ngx_queue_t *n1, const ngx_queue_t *n2)
{
    //拿到数据
    struct student *s1 = ngx_queue_data(n1, student, qnode);
    struct student *s2 = ngx_queue_data(n2, student, qnode);

    return s1->id - s2->id;
}
//遍历
void stu_foreach(const ngx_queue_t *queue)
{
    ngx_queue_t *q = ngx_queue_head(queue);

    struct student *s = ngx_queue_data(q, student, qnode);
    if (q == ngx_queue_last(queue)) {
        return;
    }   
    printf("id = %d\n",s->id);

    for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = ngx_queue_next(q)) {
        s = ngx_queue_data(q, student, qnode);
        printf("id = %d\n",s->id);
    }
}

int main(int argc,char *argv[])
{
    //初始化队列
    ngx_queue_t *q = (ngx_queue_t *)calloc(1,sizeof(ngx_queue_t));
    ngx_queue_init(q);
	//初始化数据
    struct student st1 = {1,11,111};
    struct student st2 = {2,12,112};
    struct student st3 = {3,13,113};
    struct student st4 = {4,14,114};
    struct student st5 = {5,15,115};
	//头插法 将qnode插入
    ngx_queue_insert_head(q,&st2.qnode);
    ngx_queue_insert_head(q,&st4.qnode);
    ngx_queue_insert_head(q,&st1.qnode);
    ngx_queue_insert_head(q,&st5.qnode);
    ngx_queue_insert_head(q,&st3.qnode);
    //排序
	printf("================sort=============\n");
    stu_foreach(q);
    printf("\n");
    ngx_queue_sort(q,stu_cmp);
    stu_foreach(q);
	//找中间值
    printf("================middle=============\n");
    ngx_queue_t *mid =  ngx_queue_middle(q);
    student *s = ngx_queue_data(mid, student, qnode);
    printf("id:%d\n",s->id);
	//分裂
    printf("================split=============\n");
    ngx_queue_t *n = (ngx_queue_t *)calloc(1,sizeof(ngx_queue_t));
    ngx_queue_init(n);
    ngx_queue_split(q,mid,n);
    printf("=old queue=\n");
    stu_foreach(q);
    printf("=new queue=\n");
    stu_foreach(n);
	//合并
    printf("================add=============\n");
    ngx_queue_add(q,n);
    stu_foreach(q);
    
    free(n);
    free(q);
    
    return 0;
}       
输出
在这里插入图片描述

 

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

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

相关文章

七牛云 vue 图片上传简单解说,js 上传文件图片

七牛云 vue 图片上传简单解说&#xff0c;js 上传文件图片 一、七牛云简介 首次使用七牛云存储进行项目的图片存储&#xff0c;整了一上午才整明白&#xff0c;这些官方的教程把明白人也给说糊涂了&#xff0c;文档很不规范。 七牛云有免费的使用额度&#xff0c;https://ww…

[附源码]SSM计算机毕业设计汽车租赁管理系统-JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Go学习之旅:包、变量和函数(DAY 1)

文章目录前引包、变量和函数1、包的概念和所用2、导出名或者导出函数3.1、函数参数声明方式&#xff08;一&#xff09;3.2、函数参数声明方式&#xff08;二&#xff09;4、函数返回值支持多值返回5、函数命名返回值6、变量声明7、变量的基础类型8、变量的默认值&#xff08;零…

pytorch案例代码-3

双向循环神经网络 双向循环神经网络在RNN/LSTM/GRU里都有。比如RNN cell&#xff0c;只是把h0和x1传入做线性变换产生h1继续传入同一个cell做线性变换&#xff0c;线性变换的W和b共享&#xff0c;沿着这个方向就把所有隐层和最后的输出算出来了。 那么其中的每个结点&#xff0…

android-apk解包打包

title: android-apk解包打包 categories: Android tags: [android, 加壳] date: 2022-09-28 10:29:51 comments: false mathjax: true toc: true android-apk解包打包, 以下所有操作都需要在配置好 java 环境下进行 前篇 android apk解包和打包 - https://blog.csdn.net/u0114…

(十五)Spring之面向切面编程AOP

文章目录基础环境AOP介绍AOP的七大术语切点表达式Spring的AOP的使用环境准备基于AspectJ的AOP注解式开发通知类型前置通知Before后置通知AfterReturning环绕通知Around异常通知AfterThrowing最终通知After关于JoinPoint切面的先后顺序通用切点表达式全注解式开发AOP基于XML配置…

9、前端笔记-CSS-CSS三大特性

三大特性&#xff1a;层叠性、继承性、优先级 1、层叠性&#xff08;覆盖性&#xff09; 给相同的选择器设置相同的样式&#xff0c;此时一个样式会覆盖&#xff08;层叠&#xff09;其他冲突的样式。 层叠性原则&#xff1a; 同一选择器&#xff0c;样式冲突&#xff0c;遵…

【SpringBoot】MVC配置解决跨域但仍然存在跨域

文章目录1. 跨域问题出现与解决1. 跨域问题出现与解决 检查SpringBoot中的MVC配置。 public void addCorsMappings(CorsRegistry registry) {//允许跨域访问资源定义registry.addMapping("/**")//(只允许本地的指定端口访问)允许所有.allowedOrigins("*")…

数据结构之线性表中的单链表【详解】

文章目录前言&#xff1a;一、单链表1.单链表和顺序表的优缺点2.单链表的概念和学习3.单链表的各个接口的实现&#xff08;详解每一步&#xff09;3.1.先铺垫一下大致的思路3.2.然后这边我们看一下我们大致要实现的函数有哪些3.3.接下来我们就开始实现这些代码&#xff0c;并且…

用信号量实现进程同步与互斥(含代码分析)

信号量简单的来说就是一个变量&#xff0c;代表着系统中互斥资源的数量&#xff0c;通常用原语来实现对信号量机制的操作。 一对原语&#xff1a;wait(S)也称为P操作&#xff0c;singnal(S)也称为V操作。S表示信号量 对于wait原语本身的内部逻辑代码如下&#xff08;这里以一…

黑苹果外接显示器最优解决方案

黑苹果无法外接显示器 黑苹果外接显示器解决方案 先给解决方案 电脑端 > 安装 PC端 Duet Display买个二手电视盒子40块钱左右&#xff0c;还带电源电视盒子 > 安装安卓端 Duet Display&#xff08;U盘安装就行&#xff09;电视盒子 > 用鼠标开启开发者模式双头USB&…

SpringBoot SpringBoot 原理篇 1 自动配置 1.12 bean 的加载控制【注解式】

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇1 自动配置1.12 bean 的加载控制【注解式】1.12.1 问题引入1.12.2 bean的加载…

【JVM技术专题】「原理专题」全流程分析Java对象的创建过程及内存布局

前言概要 对应过程则是&#xff1a;对象创建、对象内存布局、对象访问定位的三个过程。 对象的创建过程 对象的创建方式 java中对象的创建方式有很多种&#xff0c;常见的是通过new关键字和反射这两种方式来创建。除此之外&#xff0c;还有clone、反序列化等方式创建。 通过n…

CSS @property,让不可能变可能

本文主要讲讲 CSS 非常新的一个特性&#xff0c;CSS property&#xff0c;它的出现&#xff0c;极大的增强的 CSS 的能力&#xff01; 根据 MDN -- CSS Property&#xff0c;property CSS at-rule 是 CSS Houdini API 的一部分, 它允许开发者显式地定义他们的 CSS 自定义属性&…

vue项目分环境配置打包处理

vue项目分环境配置打包处理 目录 vue项目分环境配置打包处理 本地环境配置 生产环境配置 打包处理 打包配置处理&#xff08;扩展&#xff09; 本地环境配置 定义需要的变量&#xff0c;这里定义了各种变量标识&#xff0c;可参考使用&#xff0c;自定义项目需要的变量&…

设计问卷调查有哪些技巧?

调查问卷可以很好地帮助我们进行市场调研&#xff0c;所以想要做出一份有效的调查问卷&#xff0c;首先要确定问卷的主题。明确的主题就是定基调&#xff0c;可以让我们的后续过程更加顺利。然后我们再开始进行题目的设置和问卷的设计等动作。不过&#xff0c;在问卷设计的过程…

【js】【爬虫】fetch + sessionStorage 快速搭建爬虫环境及各种踩坑

文章目录导读需求开发环境fetch介绍为什么选择fetchfetch的封装使用数据存储数据访问封装多页面处理方案数据过大&#xff0c;拆分处理参考资料导读 需求 一说爬虫&#xff0c;很多人都会向导python&#xff0c;不过&#xff0c;真正省心的方案&#xff0c;应当是通过js控制获…

Reactive源码分析

Reactive用来绑定引用数据类型, 例如对象和数组等,实现响应式。 Reactive API 接口 export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>相关API包括readonly、createReactiveObject、shallowReadonly、isReactive、toReactive。源码运…

Eureka注册中心

文章目录一、认识服务提供者和服务调用者二、Eureka 的工作流程三、服务调用出现的问题及解决方法四、搭建 eureka-server五、注册 user-service、order-service六、在 order-service 完成服务拉取&#xff08;order 模块能访问 user 模块&#xff09;七、配置远程服务调用八、…

从入门到进阶!当下火爆的大数据技术及算法怎么还能不知道 一起来学习互联网巨头的大数据架构实践!

大数据被称为新时代的黄金和石油&#xff0c;相关技术发展迅猛,所应用的行业也非常广泛&#xff0c;从传统行业如医疗、教育、金融、旅游&#xff0c;到新兴产业如电商、计算广告、可穿戴设备、机器人等。大数据技术更是国家科技发展和智慧城市建设的基础。 当前“互联网”新业…