Nginx源码阅读1-内存池

首先我们来看一下他的一个基础组件:内存池组件。为什么先从内存池开始呢,因为后面 nginx 的内置数据结构,如:array,string 等都是从内存池分配的。

为什么需要内存池呢?在高并发的前提下,会大量地申请和释放小块的内存;虽然内核中也有相关的内存优化操作,但是还是容易出现大量地内存碎片,内存利用效率很低。如果我们一开始就申请一块大内存,自己对这一块大内存进行操作和管理,就可以高效的利用内存。

nginx 的内存池设计是十分巧妙的,我们一起来看一下,最好是后面手动实现一下。

数据结构

内存池结构体

struct ngx_pool_s {ngx_pool_data_t       d; // 存储的数据size_t                max; // 单次申请的最大内存ngx_pool_t           *current; // 当前使用的内存池ngx_chain_t          *chain; // 缓冲区链表ngx_pool_large_t     *large; // 存放大块数据的链表ngx_pool_cleanup_t   *cleanup; // 存放自定义清理函数的链表ngx_log_t            *log; // 日志
};

内存池数据结构体

typedef struct {u_char               *last; // 指向可用内存的起始地址u_char               *end; // 指向可用内存的末尾地址ngx_pool_t           *next; // 指向下一个内存池ngx_uint_t            failed; // 存储失败的次数
} ngx_pool_data_t;

大数据块结构体

struct ngx_pool_large_s {ngx_pool_large_t     *next; // 指向下一个大数据块void                 *alloc; // 大块数据
};

自定义清理函数结构体

struct ngx_pool_cleanup_s {ngx_pool_cleanup_pt   handler; // 清理函数void                 *data; // 存储的数据ngx_pool_cleanup_t   *next; // 下一个自定义清理函数结构
};

结构图

相关函数

创建内存池

ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{ngx_pool_t  *p;// 申请一块内存池的内存p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);if (p == NULL) {return NULL;}// 将可用内存的首部偏移到结构体头之后p->d.last = (u_char *) p + sizeof(ngx_pool_t);p->d.end = (u_char *) p + size;p->d.next = NULL;p->d.failed = 0;// 可用内存size = size - sizeof(ngx_pool_t);p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;p->current = p;p->chain = NULL;p->large = NULL;p->cleanup = NULL;p->log = log;return p;
}

销毁内存池

void
ngx_destroy_pool(ngx_pool_t *pool)
{ngx_pool_t          *p, *n;ngx_pool_large_t    *l;ngx_pool_cleanup_t  *c;for (c = pool->cleanup; c; c = c->next) {if (c->handler) {// 输出日志ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,"run cleanup: %p", c);// 执行清理回调c->handler(c->data);}}#if (NGX_DEBUG)/** we could allocate the pool->log from this pool* so we cannot use this log while free()ing the pool*/for (l = pool->large; l; l = l->next) {ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);}for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,"free: %p, unused: %uz", p, p->d.end - p->d.last);if (n == NULL) {break;}}#endif// 遍历销毁大数据链表for (l = pool->large; l; l = l->next) {if (l->alloc) {ngx_free(l->alloc);}}// 遍历销毁内存池for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {ngx_free(p);if (n == NULL) {break;}}
}

重置内存池

void
ngx_reset_pool(ngx_pool_t *pool)
{ngx_pool_t        *p;ngx_pool_large_t  *l;// 遍历销毁大数据链表for (l = pool->large; l; l = l->next) {if (l->alloc) {ngx_free(l->alloc);}}// 将可用指针指向结构体内存之后,重新开始写后面的内存for (p = pool; p; p = p->d.next) {p->d.last = (u_char *) p + sizeof(ngx_pool_t);p->d.failed = 0;}// 初始化pool->current = pool;pool->chain = NULL;pool->large = NULL;
}

向内存池申请内存

void *
ngx_palloc(ngx_pool_t *pool, size_t size) // 有字节对齐
{
#if !(NGX_DEBUG_PALLOC)if (size <= pool->max) {return ngx_palloc_small(pool, size, 1);}
#endif// 如果大小超过max,就申请大内存块return ngx_palloc_large(pool, size);
}void *
ngx_pnalloc(ngx_pool_t *pool, size_t size) // 无字节对齐
{
#if !(NGX_DEBUG_PALLOC)if (size <= pool->max) {return ngx_palloc_small(pool, size, 0);}
#endifreturn ngx_palloc_large(pool, size);
}

内存池加入新的块

static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{u_char      *m;size_t       psize;ngx_pool_t  *p, *new;// 当前内存池的总可用大小psize = (size_t) (pool->d.end - (u_char *) pool);// 创建一个新内存池m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);if (m == NULL) {return NULL;}new = (ngx_pool_t *) m;// 末位置new->d.end = m + psize;new->d.next = NULL;new->d.failed = 0;m += sizeof(ngx_pool_data_t);m = ngx_align_ptr(m, NGX_ALIGNMENT);// 初始位置要在需要的存储数据的内存之后new->d.last = m + size;// 加入链表for (p = pool->current; p->d.next; p = p->d.next) {if (p->d.failed++ > 4) {pool->current = p->d.next;}}p->d.next = new;return m;
}

开辟大内存块

static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{void              *p;ngx_uint_t         n;ngx_pool_large_t  *large;// 申请一块需要大小的内存p = ngx_alloc(size, pool->log);if (p == NULL) {return NULL;}n = 0;// 如果没有找到未使用的大内存块写遍历次数大于三就退出循环for (large = pool->large; large; large = large->next) {if (large->alloc == NULL) {large->alloc = p;return p;}if (n++ > 3) {break;}}// 创建一个未使用的大内存块large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);if (large == NULL) {ngx_free(p);return NULL;}large->alloc = p;large->next = pool->large;pool->large = large;return p;
}

释放大内存块

ngx_int_t
ngx_pfree(ngx_pool_t *pool, void *p)
{ngx_pool_large_t  *l;// 遍历释放for (l = pool->large; l; l = l->next) {if (p == l->alloc) {// 打印日志ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,"free: %p", l->alloc);ngx_free(l->alloc);l->alloc = NULL;return NGX_OK;}}return NGX_DECLINED;
}

添加自定义清理函数

ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{ngx_pool_cleanup_t  *c;// 申请自定义清理模块内存c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));if (c == NULL) {return NULL;}if (size) {c->data = ngx_palloc(p, size);if (c->data == NULL) {return NULL;}} else {c->data = NULL;}// 回调函数初始化为nullc->handler = NULL;c->next = p->cleanup;p->cleanup = c;// 输出日志ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);return c;
}

删除链表上的 cleanup 块

void
ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
{ngx_pool_cleanup_t       *c;ngx_pool_cleanup_file_t  *cf;for (c = p->cleanup; c; c = c->next) {if (c->handler == ngx_pool_cleanup_file) {cf = c->data;if (cf->fd == fd) {// 执行回调函数c->handler(cf);c->handler = NULL;return;}}}
}

这个 cleanup 块可以存储任何需要清理的东西,所以他就可以操作文件,内存设备等,给这个内存池提供了很大的灵活性。

删除文件回调函数

// 可以看到上面的函数有调用这个函数的if,如果调用这个,那都会被清理
void
ngx_pool_cleanup_file(void *data)
{ngx_pool_cleanup_file_t  *c = data;ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",c->fd);if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,ngx_close_file_n " \"%s\" failed", c->name);}
}

删除文件

void
ngx_pool_delete_file(void *data)
{ngx_pool_cleanup_file_t  *c = data;ngx_err_t  err;ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",c->fd, c->name);// 删除文件if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {err = ngx_errno;if (err != NGX_ENOENT) {ngx_log_error(NGX_LOG_CRIT, c->log, err,ngx_delete_file_n " \"%s\" failed", c->name);}}// 关闭文件if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,ngx_close_file_n " \"%s\" failed", c->name);}
}

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

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

相关文章

【机器学习】K近邻

2. K近邻 K近邻算法&#xff08;KNN&#xff09;的基本思想是通过计算待分类样本与训练集中所有样本之间的距离&#xff0c;选取距离最近的 K 个样本&#xff0c;根据这些样本的标签进行分类或回归。KNN 属于非参数学习算法&#xff0c;因为它不假设数据的分布形式&#xff0c…

海外合规|新加坡网络安全认证计划简介(三)-Cyber Trust

一、 认证简介&#xff1a; Cyber Trust标志是针对数字化业务运营更为广泛的组织的网络安全认证。该标志针对的是规模较大或数字化程度较高的组织&#xff0c;因为这些组织可能具有更高的风险水平&#xff0c;需要他们投资专业知识和资源来管理和保护其 IT 基础设施和系统。Cy…

开源 AI 智能名片 O2O 商城小程序:引入淘汰机制,激发社交电商新活力

摘要&#xff1a;本文深入探讨在社交电商领域中&#xff0c;开源 AI 智能名片 O2O 商城小程序如何通过设置淘汰机制&#xff0c;实现“良币驱逐劣币”&#xff0c;激励士气&#xff0c;为社交电商企业注入新的活力。通过分析缺乏淘汰机制的弊端以及设置淘汰机制的优势&#xff…

用python发送邮件

用python发送邮件需要smtplib&#xff0c;email包,例子如下&#xff1a; import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipartdef send_email():# 邮件的基本信息sender_email "xx.com" # 发送方邮箱receiver_e…

CAAC无人机飞行执照理论培训课程详解

CAAC&#xff08;中国民用航空局&#xff09;无人机飞行执照的理论培训课程是确保无人机飞手全面掌握飞行和应用技能的重要环节。以下是对该理论培训课程的详细解析&#xff1a; 一、课程目标 理论培训课程的主要目标是使学员&#xff1a; 了解并掌握无人机相关的法律法规、…

Java基于微信小程序的家庭财务管理系统,附源码

博主介绍&#xff1a;✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不…

EmguCV学习笔记 VB.Net 8.4 pyrMeanShiftFiltering

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

Spark2.x 入门:逻辑回归分类器

方法简介 逻辑斯蒂回归&#xff08;logistic regression&#xff09;是统计学习中的经典分类方法&#xff0c;属于对数线性模型。logistic回归的因变量可以是二分类的&#xff0c;也可以是多分类的。 示例代码 我们以iris数据集&#xff08;iris&#xff09;为例进行分析。i…

Java项目:137 springboot基于springboot的智能家居系统

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本基于Springboot的智能家居系统提供管理员、用户两种角色的服务。 总的功能个人中心、基础数据管理、家具管理、任务管理和用户管理。本系统…

显微镜基础知识--脑机起步

一、显微镜类别 学生级、实验级、研究级生物显微镜单目型、双目型、三目型生物显微镜 二、显微镜基础原理 &#xff08;1&#xff09;光学显微镜 光学显微镜主要由目镜、物镜、载物台和反光镜(集光镜)组成。目镜和物镜都是凸透镜&#xff0c;焦距不同。物镜的凸透镜焦距小于…

Web攻防之应急响应(二)

目录 前提 &#x1f354;学习Java内存马前置知识 内存马 内存马的介绍 内存马的类型众多 内存马的存在形式 Java web的基础知识&#xff1a; Java内存马的排查思路&#xff1a; &#x1f354;开始查杀之前的需要准备 1.登录主机启动服务器 2.生成jsp马并连接成功 …

MATLAB 仿真跳频扩频通信系统

1. 简介 跳频扩频&#xff08;FHSS&#xff09;是一种通过在不同的频率之间快速切换来对抗窄带干扰的技术。在这篇博客中&#xff0c;我们将使用 MATLAB 进行 FHSS 通信系统的仿真&#xff0c;模拟跳频过程、调制、解调以及信号在不同步骤中的变化。通过对仿真结果进行可视化&…

python-简单的dos攻击

前言 这个是DOS攻击学习(注意&#xff1a;千万别去攻击有商业价值的服务器或应用&#xff0c;不然会死的很惨(只有一个IP通过公网访问容易被抓),前提是网站没有攻击防御) 创建一个以python编写的后端web服务(好观察) 安装flask pip install flask from flask import Flaskapp …

打卡第60天------图论

加油&#xff01;尽管前面的道路很困难&#xff0c;但是依然要坚持下去✊。 在算法训练营我学到了很多东西&#xff0c;对于算法的方法来说真的是涨知识了&#xff0c;对于我一个非科班出身&#xff0c;半路转行的干IT的人来说真的给予了我很大的帮助。我会继续回头看代码随想录…

前端入门了解

1. 网页 1.1 网页概述 1.2 超文本标记语言 1.3 网页的形成 2. 浏览器了解 网页需要通过浏览器来展示&#xff0c;下面是关于浏览器的两点; 国际上通用的浏览器有如下六个&#xff08;百度&#xff0c;360&#xff0c;uc等是主要在国内使用&#xff09;&#xff0c; 3. We…

Apple 重發iOS 17.6.1 修正版

蘋果又再次替 iPhone 和 iPad 用戶釋出 iOS 17.6.1更新&#xff08;21G101&#xff09;&#xff0c;這次更新與8日所推出的 iOS 17.6.1 正式版相同&#xff0c;官方在更新說明內也沒有提到任何修改&#xff0c;依舊是維持修正進階資料保護的問題 iOS 17.6.1 更新修正版內容重點…

面向切面编程

1. 增删改查 三层干啥 2. 文件上传 第三方api(接口) 调用准备 -- 官方代码 -- 工具类 3. 登录鉴权 登录(查询 密码) 鉴权(拦截器)登录完成后--后台生成token返回给浏览器--每次浏览器发请求时候携带token--服务器需要使用拦截器拦截请求,获取token--校验token生成 token校…

借老系统重构机会我写了个groovy规则引擎

公司老系统的重构计划早就有了&#xff0c;为了对Java硬编码的各种校验规则进行重构&#xff0c;特地参考了相关技术&#xff0c;最终选择了groovy进行了系统的学习&#xff0c;并编写了一个即插即用的轻量级规则引擎。 文章目录 项目背景技术选型groovy的性能groovy脚本执行线…

【拉取Git项目到本地,知识小记,后续再改】

前提&#xff1a;Git已经安装好 https://blog.csdn.net/mukes/article/details/115693833 安装至步骤2.2.4即可 第一步创建本地项目目录 第二步获取他人提供的项目git地址或者自己在网上找的他人项目的git地址 Git 全局设置: git init git config --global user.name “ASxx”…

1998-2023年上市公司金融/信贷/资本资源错配程度数据(含原始数据+计算代码+结果)

1998-2023年上市公司金融/信贷/资本资源错配程度数据&#xff08;含原始数据计算代码结果&#xff09; 1、时间&#xff1a;1998-2023年 2、来源&#xff1a;上市公司年报 3、指标&#xff1a;证券代码、year、应付账款、负债合计、利息支出、行业代码、是否ST或PT、上市日期…