nginx 内存管理(一)

文章目录

    • 前提知识
    • nginx内存管理的基础
      • 内存分配
        • 不初始化封装malloc
        • 初始化malloc
    • 内存池
      • 内存池结构
      • 清理函数cleanup
      • 大块内存large
    • 创建内存池
    • 申请内存
      • void *ngx_palloc(ngx_pool_t *pool, size_t size)
      • void *ngx_pnalloc(ngx_pool_t *pool, size_t size)
      • void *ngx_pcalloc(ngx_pool_t *pool, size_t size)
    • 内存释放
      • 大块内存释放 ngx_pfree(单个大块内存)
      • 释放内存池

前提知识

应用程序的内存可以简单的氛围堆内存,栈内存;对于栈内存,函数编译的时候,编译器会移动栈到当前指针位置的代码,实现栈空间的管理。但是对于堆内存,通常需要程序员进行管理,我们常说的内存管理其实就是对堆空间的管理。

内存对齐

nginx的内存管理可以分为两个部分,一种是常规的内存池,也就是进程平时所需要的内存管理;一种是共享内存的管理。这里主要讲的是内存池管理内存

nginx内存管理的基础

nginx的内存管理包括1三个部分:内存分配、内存对齐、内存释放

  • 分配:nginx的内存分配本质上是对c语言的malloc进行封装,详情可以查看src/os/unix/ ngx_alloc.c,其分配包括两种形式:

    • .void *ngx_alloc(size_t size, ngx_log_t *log);//不初始化
    • .void *ngx_calloc(size_t size, ngx_log_t *log);//对分配空间初始化为0
  • 对齐:参考上文链接

  • 释放:#define ngx_free free // 见os/unix/ ngx_alloc.h,宏定义。

内存分配

不初始化封装malloc
void *ngx_alloc(size_t size, ngx_log_t *log)
{  void  *p;  p = malloc(size);   if (p == NULL) {     错误信息处理  ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "malloc(%uz) failed", size);  }   //调试信息处理 ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);   return p;
}
初始化malloc
void *ngx_calloc(size_t size, ngx_log_t *log)
{   void  *p;   p = ngx_alloc(size, log);   //1.Nginx分配if (p) {      ngx_memzero(p, size);  //2.若分配成功,则Nginx初始化}   return p;
}

内存池

nginx使用内存池来管理内存,当进程需要内存时,向内存池申请,使用后释放内存池。申请的内存主要分为两种:小块内存,大块内存

  • 小块内存:使用后不需要(立即)释放,可以等到释放内存池的时候再释放;
  • 大块内存:使用后可以调用相关接口进行释放,也可以等待内存池释放再释放。

内存池释放的时候,可以调用回调清理函数,可以有多个回调函数,这些回调函数构成链表进行管理。

内存池结构

内存池的结构如下:一般来说,内存池的第一个结点会包含以下的数据结构,而从第二个结点开始,max——log部分会被缺省,当做空间进行分配。
在这里插入图片描述

其数据结构如下(详情查看:core/ngx_palloc.h):

typedef struct ngx_pool_s            ngx_pool_t;typedef struct {    u_char               *last;//下一次分配的开始u_char               *end;//内存池结束的位置ngx_pool_t           *next;//内存池的内存块通过这个连成链表ngx_uint_t            failed;//内存池分配失败的次数
} ngx_pool_data_t;//d:数据块struct ngx_pool_s {  ngx_pool_data_t       d;//指向一个内存池的第一个数据块size_t                max;//判断大内存块和小内存块的标准ngx_pool_t           *current;//指向当前的内存池ngx_chain_t          *chain;//挂接一个ngx_chain_t结构ngx_pool_large_t     *large;//指向大块内存链表的第一个结点,即分配控件超过max的内存ngx_pool_cleanup_t   *cleanup;//指向清理函数的管理结点ngx_log_t            *log;//用来记录日志信息
};

在这里插入图片描述

清理函数cleanup

struct ngx_pool_cleanup_s {   ngx_pool_cleanup_pt   handler;//指向清理函数的地址void                 *data;//data:用于向数据清理函数传递的参数,指向待清理的数据的地址,若没有则为NULLngx_pool_cleanup_t   *next;//指向下一个清理函数的管理结点
};//清理函数指针
typedef void (*ngx_pool_cleanup_pt)(void *data);

在这里插入图片描述

大块内存large

typedef struct ngx_pool_large_s  ngx_pool_large_t;
struct ngx_pool_large_s {   ngx_pool_large_t     *next;//指向分配的大块内存的下一个管理结点void                 *alloc;//指向大块内存的地址
};

在这里插入图片描述

创建内存池

ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log)//创建内存池,size用来确定max
  1. 申请内存,如果操作系统支持对齐,按16字节对齐方式对齐
  2. 如果成功申请内存,按以下3-5步骤初始化内存池ngx_pool_t 结构,否则返回NULL
  3. 初始化ngx_pool_t 的ngx_pool_data_t类型成员d;
  4. 初始化ngx_pool_t的max ;
  5. 初始化ngx_pool_t的current ;
  6. 将ngx_pool_t的chain、 large以及cleanup初始化为NULL;
  7. 初始化ngx_pool_t的log成员;
  8. 返回内存池指针(首地址)p。

在这里插入图片描述

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->log = log;
return p;

申请内存

  1. void *ngx_palloc(ngx_pool_t *pool, size_t size):基本方法
  2. void *ngx_pnalloc(ngx_pool_t *pool, size_t size):小内粗块不考虑对齐的palloc
  3. void *ngx_pcalloc(ngx_pool_t *pool, size_t size):含有初始化

void *ngx_palloc(ngx_pool_t *pool, size_t size)

{//判别大小块内存方式if (size <= pool->max) {        return ngx_palloc_small(pool, size, 1);//对齐方式,申请小块内存,1表示对齐,0表示不对齐 }return ngx_palloc_large(pool, size);
}

void *ngx_pnalloc(ngx_pool_t *pool, size_t size)

{//判别大小块内存方式If (size <= pool->max) {        return ngx_palloc_small(pool, size, 0);//不对齐方式,申请小块内存  }return ngx_palloc_large(pool, size);
}

void *ngx_pcalloc(ngx_pool_t *pool, size_t size)

{  void *p;    p = ngx_palloc(pool, size);  //调用 ngx_pallocif (p) //初始化处理{     ngx_memzero(p, size); }  return p;
}
  • 当小块内存进行分配的时候,会先从 last指针 所对应的地址开始查找,查找能够满足size大小的内存;如果查找成功,last+=size;如果查找失败,则failed++,并且根据next来遍历整个链表,查找能够分配size的内存块;如果遍历完整个链表,都没有找到对应的内存块,则重新申请内存再分配。
static ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align){do{  m = p->d.last;    //注意这个写法,d是p的非指针成员,last是d的指针成员  if (align) {    m = ngx_align_ptr(m, NGX_ALIGNMENT);  //需要对齐,则对齐处理     }     if ((size_t) (p->d.end - m) >= size) //满足大小要求,划出分配区域{       p->d.last = m + size;     //划出size大小    return m;     }    p = p->d.next;  //下一个内存块,遍历所有内存块} while (p);return ngx_palloc_block(pool, size);//申请新的内存块,完成该次小内存块申请
}
  • 当申请大块内存时:要注意大块内存的管理结点,也就是ngx_pool_large_t是存放在小块内存中的,而大块内存的地址是存放在管理地址的alloc指针中的;大块内存在释放的时候,会把alloc所在地址的内存先释放掉,管理结点的内存可以暂时不释放,等到内存池释放的时候再一起释放;小块内存中可能会存在很多空闲的大块内存的管理结点,如果大块内存在申请时遇到空闲的管理结点可以直接挂接在空闲的管理结点上,如果没有空闲的就按照申请小块内存的方式申请一个结点再挂接。
    • 大块内存申请,直接用ngx_alloc申请size大小的内存(p指向);将该大块内存(p指向)链入有关队列;
    • 步骤一:从内存池中查找是否有空闲的ngx_pool_large_t类型large结点,有则直接用该结点链接管理以上步骤申请的大内存块(p指向);若没有则进行以下第2步骤;
    • 步骤二:从内存池中按小块内存方式申请一个ngx_pool_large_t结点large,若失败,释放1步骤的大内存块,返回;如果成功,按序进行以下第3、4步骤;
    • 步骤三:将大内存块(p指向)链接入large中(将p赋值为large的alloc);
    • 步骤四:将large链入内存池结构的large队列中,并将其作为队列头部

在这里插入图片描述

static void *ngx_palloc_large(ngx_pool_t *pool, size_t size)//大块
……
p = ngx_alloc(size, pool->log);//直接申请大内存
……//含有n=0语句
for (large = pool->large; large; large = large->next)//循环查找空闲large
{        //步骤1if (large->alloc == NULL) //找到空闲large{         large->alloc = p;        //将大内存链入该空闲largereturn p;      } if (n++ > 3) {            break;        }//次数限制为4次?
}large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
//申请新空白large,步骤2
if (large == NULL) {    //失败,释放大内存,返回ngx_free(p);      return NULL;    }large->alloc = p;   //将大块内存链入large,步骤3large->next = pool->large;    //将新large链入内存池large链头,步骤4
pool->large = large;

内存释放

  • 大块内存,Nginx提供了专门的接口-ngx-free来释放;
  • 小块内存,Nginx将它们的释放连同内存池释放一起进行;
  • 先释放大块内存,再释放小块内存

大块内存释放 ngx_pfree(单个大块内存)

  1. 找到内存池的large(链头)指针;
  2. 遍历该链头指向的链表,逐个对每个large结点判定该large的alloc指向的大内存块是否就是需要释放的块,是则调用ngx_free(宏)释放该大内存块,置该结点的alloc为NULL;不是,继续查找和判定。
ngx_int_tngx_pfree(ngx_pool_t *pool, void *p)for (l = pool->large; l; l = l->next) //遍历内存池的large
{  if (p == l->alloc)   //判定该large结点是否含有待释放大块内存{      //调试信息     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,  "free: %p", l>alloc);         ngx_free(l->alloc);  //释放该大块      l->alloc = NULL; //置该结点alloc为NULL    return NGX_OK;      }   }

释放内存池

  1. 释放清理函数:要先对内存进行有关处理
    • 从内存池cleanup指针开始遍历;
    • 对每个结点清理函数,调用一次该清理函数。
  2. 释放大块内存(全部大块内存)
    • 从内存池large指针开始遍历;
    • 每个结点如果含有大块内存,则释放该大块内存
  3. 释放内存块
    • 从内存池d->next指针开始遍历,释放每个结点内存块
voidngx_destroy_pool(ngx_pool_t *pool)//先释放清理函数,因为需要先对内存进行有关处理
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);   //调用一次该清理函数 }  }for (l = pool->large; l; l = l->next)//遍历每个大内存结点
{  if (l->alloc) //如果该结点有大内存{         ngx_free(l->alloc);    //释放该结点大内存}   
}for (p = pool, n = pool->d.next; ; p = n, n = n->d.next){     //遍历每个内存池内存块结点  ngx_free(p);   //释放该内存块结点if (n == NULL) //如果是结尾,终止循环{            break;        }    
}

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

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

相关文章

C进阶-语言文件操作

本章重点&#xff1a; 什么是文件 文件名 文件类型 文件缓冲区 文件指针 文件的打开和关闭文件的顺序读写文件的随机读写文件结束的判定 1. 什么是文件 磁盘上的文件是文件。 但是在程序设计中&#xff0c;我们一般谈的文件有两种&#xff1a;程序文件、数据文件 1.1 程序文件…

【Django 01】环境搭配与项目配置

1. 介绍 https://github.com/Joe-2002/sweettalk-django4.2#readme django 学习笔记【2023-10-16】 - 掘金 Django 是一个使用 Python 编写的开源 Web 应用程序框架&#xff0c;它提供了一套用于快速开发安全、 可扩展和高效的 Web 应用程序的工具和功能。Django 基于…

三级等保-linux服务器三权分立设置

安全问题 安全控制点 风险分析 风险等级 标准要求 加固建议 服务器未严格按照系统管理员权限、审计管理员权限、安全管理员权限进行分配管理员账户&#xff0c;未实现管理员用户的最小权限划分。 访问控制 可能存在管理员越权操作的风险 中 d)应授予管理用户所需的最…

虹科活动 | 探索全新AR应用时代,虹科AR VIP研讨会广州场回顾!

文章来源&#xff1a;虹科数字化AR 阅读原文&#xff1a;https://mp.weixin.qq.com/s/7tmYR42Tw5XLn70fm8Nnew 主题演讲 本次研讨会&#xff0c;虹科特邀 “工业AR鼻祖” 美国Vuzix公司的首席应用工程师郑慎方先生进行主题演讲&#xff0c;并邀请到了各界的专业人士和企业代表参…

一款.NET Core开源的基于Vue+ElementUI开发的博客系统 - StarBlog

前言 今天给大家推荐一款.NET Core开源的基于VueElementUI开发的博客系统 - StarBlog。该项目配套详细的文章教程&#xff0c;可以作为 .Net Core 入门项目学习。 官方项目介绍 StarBlog支持Markdown导入的博客。后端基于最新的.Net6和Asp.Net Core框架&#xff0c;遵循REST…

【python】什么是网络爬虫?

什么是网络爬虫&#xff1f; 网络爬虫是一种自动化程序&#xff0c;用于从互联网上抓取信息。这些信息可以是文本、图像、视频、数据表格等各种形式的数据。爬虫程序通过模拟浏览器的行为&#xff0c;自动访问网页、抓取内容&#xff0c;并将其保存或处理。这对于数据挖掘、搜索…

idea设置字体大小快捷键 Ctrl+鼠标上下滑 字体快捷键缩放设置

双击 按住ctrl鼠标滑轮上划放大就好了 这个双击设置为&#xff0c;Ctrl鼠标下滑 字体缩小就好了

爬虫基础 JS逆向

爬虫核心 1. HTTP协议与WEB开发 1. 什么是请求头请求体&#xff0c;响应头响应体 2. URL地址包括什么 3. get请求和post请求到底是什么 4. Content-Type是什么 &#xff08;1&#xff09;简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;…

k8s 暴露pod

kubenretes中暴露Pod及Service的6种方式 &#xff0c;分别为port_forward、hostNetwork、hostPort、nodePort、loadBalancer、Ingress。 下面讲下nodeport nodePort Kubernetes中的service默认情况下都是使用的ClusterIP这种类型&#xff0c;这样的service会产生一个Cluster…

unity(WebGL) 截图拼接并保存本地,下载PDF

截图参考&#xff1a;Unity3D 局部截图、全屏截图、带UI截图三种方法_unity 截图_野区捕龙为宠的博客-CSDN博客 文档下载&#xff1a; Unity WebGL 生成doc保存到本地电脑_unity webgl 保存文件_野区捕龙为宠的博客-CSDN博客 中文输入&#xff1a;Unity WebGL中文输入 支持输…

福昕阅读器打开pdf文档时显示的标题不是文件名

0 Preface/Foreword 1 现象 文件名为&#xff1a;Demo-20231017 打开效果&#xff1a;显示名字为 word template 2 解决方法 2.1 利用打印方式将word生产pdf 在word生成pdf文件时&#xff0c;使用打印方式生成pdf文档。 2.2 删除word文档设置的标题 文件---》信息---》标…

推荐《全职猎人》

电视动画《全职猎人》是由MADHOUSE公司制作的长篇电视动画&#xff0c;改编自日本漫画家富坚义博创作的同名漫画。该动画于2011年10月2日—2014年9月23日在日本电视网协议会首播&#xff0c;全148话。 剧场版动画《全职猎人&#xff1a;绯色的幻影》和《全职猎人&#xff1a;最…

计算机缺失d3dcompiler_47.dll解决方案,如何修复电脑缺失d3d文件

在计算机系统中&#xff0c;DLL文件&#xff08;动态链接库&#xff09;是一种重要的共享库&#xff0c;它包含了可被多个程序使用的代码和数据。然而&#xff0c;当某些DLL文件丢失或损坏时&#xff0c;可能会导致程序无法正常运行。本文将介绍四种解决D3DCompiler_47.dll缺失…

二、vue基础语法

一、模板语法 1、文本渲染 使用双花括号语法插入文本 <template><div><h3>msg: {{ message }}</h3></div> </template><script> export default {data() {return {message: "输出信息"}} } </script><style s…

C语言柔性数组

大家好&#xff0c;我们今天来补充一个知识&#xff0c;就是柔性数组。 柔性数组概念&#xff1a; 也许你从来没有听说过柔性数组&#xff08;flexible array&#xff09;这个概念&#xff0c;但是它确实是存在的。 C99 中&#xff0c;结构中的最后一个元素允许是未知大小的数…

隐式类型转换

什么是隐式类型转换&#xff0c;多参数的造函数隐式类型转换&#xff0c;和单参数的构造函数隐式类型转换有什么区别 C中有三种主要的隐式类型转换&#xff1a; 1:多参数的构造函数隐式类型转换 2:单参数的构造函数隐式类型转换 3:成员函数隐式类型转换。…

【企业级SpringBoot单体项目模板 】—— 项目代码管理

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;SpringBoot项目模版、企业级、模版、代码管理☀️每日 一言&#xff1a;生命力顽强的种子&#xff0c;从不对瘠土唱诅咒的歌。 文章目录 一、第一种&#xff1a;先创建仓库1.1 创建仓库1.2 clone…

黑白棋(Othello, ACM/ICPC World Finals 1992, UVa220)rust解法

你的任务是模拟黑白棋游戏的进程。黑白棋的规则为&#xff1a;黑白双方轮流放棋子&#xff0c;每次必须让新放的棋子“夹住”至少一枚对方棋子&#xff0c;然后把所有被新放棋子“夹住”的对方棋子替换成己方棋子。一段连续&#xff08;横、竖或者斜向&#xff09;的同色棋子被…

jvm的jshell,学生的工具

jshell 在我眼里&#xff0c;只能作为学校教学的一个玩具&#xff0c;事实上官方也做了解释&#xff0c;以下是官方的解释&#xff1a; 在学习编程语言时&#xff0c;即时反馈很重要&#xff0c;并且 它的 API。学校引用远离Java的首要原因 教学语言是其他语言有一个“REPL”…

【C++】:类和对象(中)之类的默认成员函数——构造函数and析构函数

1.类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器会生成…