Nginx高级数据结构动态数组源码剖析

本博客于学习nginx时刚好看了几眼nginx动态数组的实现源码,故记录一下

其实动态数组的实现原理核心都大差不差,推荐看看C++ STL(SGI)的vector更好

类型定义

typedef struct { //可以通过ngx_array_create函数创建空间,并初始化各个成员void        *elts; //数组地址ngx_uint_t   nelts; //已经使用数量size_t       size; //数组每一个元素的内存字节大小ngx_uint_t   nalloc; //数组长度ngx_pool_t  *pool; //用于分配内存的内存池
} ngx_array_t;

相关api源码剖析

ngx_array_create创建动态数组

  • p:创建数组时传入所属内存池

  • n:ngx_array_tnalloc成员(数组长度的初始大小)

  • size:ngx_array_tsize(数组元素的大小)

//开辟n个size空间
ngx_array_t *
ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size)
{ngx_array_t *a;//1.在内存池中分配一块ngx_array_t结构体大小的内存a = ngx_palloc(p, sizeof(ngx_array_t));if (a == NULL) {return NULL;}//初始化结构体if (ngx_array_init(a, p, n, size) != NGX_OK) {return NULL;}return a;
}

ngx_array_init初始化动态数组

  • ngx_array_create调用传入数组指针,内存池指针,初始数组大小,以及每个元素的大小
static ngx_inline ngx_int_t
ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{//初始化相关参数array->nelts = 0;array->size = size;array->nalloc = n;array->pool = pool;//为数组分配内存,大小为 数组长度*数组元素大小array->elts = ngx_palloc(pool, n * size);if (array->elts == NULL) {return NGX_ERROR;}return NGX_OK;
}

ngx_array_destroy销毁动态数组

nginx内存池源码剖析

这个函数源码实现涉及到了内存池的芝士,细节不懂的先去看看nginx的内存池的源码

由于nginx内存池本身的设计问题, 对于小块内存不具备释放操作,但是ngx_array_destroy函数通过变通,可以实现释放操作(前提是ngx_array_t所属的内存池只被这个ngx_array_t使用,否则不一定能满足释放条件)

由于在ngx_array_t在创建时(ngx_array_createngx_array_init),是先从内存池中分配ngx_array_t结构体*,然后分配动态数组的内存*,所以释放时将顺序反过来,先尝试释放动态数组的内存,在释放ngx_array_t结构体的内存

void
ngx_array_destroy(ngx_array_t *a)
{ngx_pool_t  *p;p = a->pool;//如果动态数组刚好是内存池分配的最后一块内存,则通过移动last指针释放if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {p->d.last -= a->size * a->nalloc;}//如果ngx_array_t结构体刚好是内存池分配的最后一块内存,则通过移动last指针释放if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {p->d.last = (u_char *) a;}
}

ngx_array_push添加一个元素

ngx_array_push函数分配内存后,返回一个元素的地址,用户通过这个地址在赋值

//检查array数组的elts元素释放已经用完,如果已经用完,则再重新开辟array空间来存储
//ngx_array_push从数组中获取一个数组成员,ngx_array_push_n为一次性获取n个
void *
ngx_array_push(ngx_array_t *a)
{void        *elt, *new;size_t       size;ngx_pool_t  *p;if (a->nelts == a->nalloc) {//如果数组已经全部使用了,分配内存size = a->size * a->nalloc;//数组占用的内存的字节大小p = a->pool;if ((u_char *) a->elts + size == p->d.last&& p->d.last + a->size <= p->d.end){/*
如果当前内存池中剩余的空间大于或者等于本次需要新增的空间,那么本次扩容将只扩充新增的空间。例如,对于ngx_array_push方法来说,
就是扩充1个元素,而对于ngx_array_push_n来说,就是扩充n个元素。*/p->d.last += a->size;a->nalloc++;} else {
/* allocate a new array 
如果当前内存池中剩余的空间小于本次需要新增的空间,那么对ngx_array_push方法来说,会将原先动态数组的容量扩容一倍,而对于
ngx_array_push_n来说,情况更复杂一些,如果参数n小于原先动态数组的容量,将会扩容一倍;如果参数n大于原先动态数组的容量,
这时会分配2×n大小的空间,扩容会超过一倍。
*/new = ngx_palloc(p, 2 * size);if (new == NULL) {return NULL;}ngx_memcpy(new, a->elts, size);a->elts = new;a->nalloc *= 2;}}//通过偏移量定位到分配元素的地址,返回地址elt = (u_char *) a->elts + a->size * a->nelts;a->nelts++;//增加已使用数量return elt;
}

ngx_array_push_n添加N个元素

//ngx_array_push从数组中获取一个数组成员,ngx_array_push_n为一次性获取n个
void *
ngx_array_push_n(ngx_array_t *a, ngx_uint_t n)
{void        *elt, *new;size_t       size;ngx_uint_t   nalloc;ngx_pool_t  *p;size = n * a->size;if (a->nelts + n > a->nalloc) {/* the array is full */p = a->pool;if ((u_char *) a->elts + a->size * a->nalloc == p->d.last&& p->d.last + size <= p->d.end){/** the array allocation is the last in the pool* and there is space for new allocation
如果当前内存池中剩余的空间大于或者等于本次需要新增的空间,那么本次扩容将只扩充新增的空间。例如,对于ngx_array_push方法来说,
就是扩充1个元素,而对于ngx_array_push_n来说,就是扩充n个元素。*/p->d.last += size;a->nalloc += n;} else {/* allocate a new array 如果当前内存池中剩余的空间小于本次需要新增的空间,那么对ngx_array_push方法来说,会将原先动态数组的容量扩容一倍,而对于ngx_array_push_n来说,情况更复杂一些,如果参数n小于原先动态数组的容量,将会扩容一倍;如果参数n大于原先动态数组的容量,这时会分配2×n大小的空间,扩容会超过一倍。*/nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc);new = ngx_palloc(p, nalloc * a->size);if (new == NULL) {return NULL;}ngx_memcpy(new, a->elts, a->nelts * a->size);a->elts = new;a->nalloc = nalloc;}}elt = (u_char *) a->elts + a->size * a->nelts;a->nelts += n; return elt;
}

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

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

相关文章

GitLab/Github从头开始配置秘钥

1、下载git安装包 CNPM Binaries Mirrorhttps://registry.npmmirror.com/binary.html?pathgit-for-windows/ 拉到页面最底部选择 点进文件夹下载32位或者64位的版本&#xff0c;我的是64位就选择64的版本进行安装 2、傻瓜式安装 3、在相应的文件夹右键选择 UserName为你的用…

【vSphere】更改集群中ESXi主机IP地址的安全方法

参考步骤&#xff08;不完整验证&#xff09; 将 ESXi 主机置于维护模式。从 vCenter 中移出集群&#xff0c;并删除 ESXi 主机。使用 ESXi 主机 UI Client 或 KVM 直接连接。更改 IP 地址并更新 DNS 服务器。将 ESXi 主机添加回 vCenter Server&#xff0c;置于维护模式&…

Flutter-自定义图片3D画廊

效果 需求 3D画廊效果 设计内容 StackGestureDetectorTransformPositioned数学三角函数 代码实现 具体代码大概300行 import dart:math;import package:flutter/material.dart; import package:flutter_xy/widgets/xy_app_bar.dart;import ../../r.dart;class ImageSwitc…

用C语言打造自己的Unix风格ls命令

在Unix或类Unix操作系统中&#xff0c;ls是一个非常基础且实用的命令&#xff0c;它用于列出当前目录或指定目录下的文件和子目录。下面&#xff0c;我们将通过C语言编写一个简化的ls命令&#xff0c;展示如何利用dirent.h头文件提供的函数接口实现这一功能。 c #include &quo…

【3DsMax】UVW展开——以制作牙膏盒为例

效果 步骤 1. 从网上下载牙膏盒贴图&#xff0c;我下载的贴图地址为&#xff08;牙膏盒贴图链接&#xff09; 2. 打开3DsMax&#xff0c;创建一个长方体&#xff0c;设置长宽高分别为180、45、40毫米 打开材质编辑器&#xff0c;点击漫反射后的按钮 双击“位图” 将材质赋予长…

阿里云云服务器ECS端口多个端口号开通教程

阿里云云服务器ECS端口多个端口号开通教程 1、登录到ECS云服务器管理控制台 2、左侧栏找到【实例与镜像】>>【实例】&#xff0c;找到目标ECS实例&#xff0c;点击实例ID进入到实例详情页 3、切换到【安全组】页面&#xff0c;点击右侧【配置规则】&#xff0c;如下图&…

HTML静态网页成品作业(HTML+CSS)——抗击疫情网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

图论题目集一(代码 注解)

目录 题目一&#xff1a; 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目一&#xff1a; #include<iostream> #include<queue> #include<cstring> using namespace st…

<商务世界>《第15课 投标文件一般包含的子文件》

1 响应文件封面 招标文件中的响应文件封面是投标人或参与者在提交响应文件时所使用的封面设计。这个封面不仅仅是文件的外包装&#xff0c;更是投标人形象和专业素质的直观展示&#xff0c;对于给招标方留下良好的第一印象至关重要。 首先&#xff0c;响应文件封面通常会包含…

java中下载多个文件和文件夹打压缩包下载,并自定义包中每个文件的名称

我的需求是一个可以批量下载文件或文件夹的接口&#xff0c;下载一个文件就正常下载&#xff0c;下载多个文件或单个多个文件夹都压缩成zip下载 本来想的是直接用hutool里面的ziputil工具类就行&#xff0c;但是我这里报错的文件都是用随机字符串命名的&#xff0c;直接用ZipUt…

硬盘哨兵Hard Disk Sentinel Pro V6.20.0.0 便携版

Hard Disk Sentinel 是一款功能强大的硬盘监控和分析软件&#xff0c;专为 Windows 用户设计。它可以实时监测硬盘驱动器&#xff08;HDD&#xff09;、固态硬盘&#xff08;SSD&#xff09;、混合硬盘&#xff08;SSHD&#xff09;、NVMe SSD、RAID 数组和外部 RAID 盒子的健康…

uniapp可视范围高度 - 用户屏幕可操作的屏幕高度 - 适用于APP、H5@公众号、纯H5@Chrome

可视范围高度 let heightPx uni.getWindowInfo().windowHeight uni.getWindowInfo().windowTop 官方手册 uni.getWindowInfo() | uni-app官网uni-app,uniCloud,serverless,uni.getWindowInfo()https://uniapp.dcloud.net.cn/api/system/getWindowInfo.html 实测数据 uni.ge…

【精准】北斗同步时钟(北斗卫星授时服务器)助力医疗信息化

【精准】北斗同步时钟&#xff08;北斗卫星授时服务器&#xff09;助力医疗信息化 【精准】北斗同步时钟&#xff08;北斗卫星授时服务器&#xff09;助力医疗信息化 北斗时钟同步服务器是一款支持NTP和SNTP网络时间同步协议&#xff0c;高精度、大容量、高品质的高科技时钟产品…

十、MySQL主从架构配置

一、资源配置 主库&#xff1a;192.168.134.132 从库&#xff1a;192.168.134.133 从库&#xff1a;192.168.134.134 二、主从同步基本原理&#xff1a; master用户写入数据&#xff0c;会生成event记录到binary log中&#xff0c;slave会从master读取binlog来进行数据同步…

Java安全 反序列化(1) URLDNS链原理分析

Java安全 反序列化(1) URLDNS链原理分析 文章目录 Java安全 反序列化(1) URLDNS链原理分析前置知识应用分析payload1.新建HashMap类2.新建URL类3.获取URL 的 Class对象4.通过反射访问URL内部变量5.通过反射为URL中类赋值6.调用HashMap#put方法传入key和value7.再次通过反射为UR…

铝壳电阻的工艺结构原理及选型参数总结

🏡《总目录》 目录 1,概述2,工作原理3,结构特点4,工艺流程4.1,原材料准备4.2,点胶4.3,高温烘烤4.4,组合4.5,焊接4.6,表面处理4.7,电气性能测试5,选型参数5.1,阻值(Resis

ocp考试是中文还是英文?ocp认证好考吗

ocp认证是中文还是英文考试ocp认证的考试常用语种是英文&#xff0c;除开英文之外还有日语等语种&#xff0c;但是目前没有中文(12c的时候有过中文考试)&#xff0c;所以考生最好具有一定的英语水平再报名参加考试&#xff0c;ocp认证考试的形式为机试&#xff0c;考试的题型全…

面向未来的前沿人工智能监管

策制定者应该为未来十年人工智能系统更加强大的世界做好准备。这些发展可能会在人工智能科学没有根本性突破的情况下发生&#xff0c;只需扩展当今的技术以在更多数据和计算上训练更大的模型即可。 用于训练前沿人工智能模型的计算量在未来十年可能会显着增加。到 2020 年代末…

kafka什么情况下会认为发送失败进而去重试

在Kafka中&#xff0c;发送消息的过程是异步的&#xff0c;即消息后不会立即得到发送结果。Kafka会将消息添加到发送缓冲区&#xff0c;并立即返回一个成功的响应。因此&#xff0c;Kafka并不会直接知道消息是否成功发送到了目标主题的分区。 Kafka在以下情况下会认为发送失败…

03.生命周期和工程化开发入门

一、Vue生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09;什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a;就是一个Vue实例从创建 到 销毁 的整个过程。 生命…