nginx 内存管理(一)

news2024/11/15 15:48:38

文章目录

    • 前提知识
    • 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:用于向数据清理函数传递的参数,指向待清理的数据的地址,若没有则为NULL
	ngx_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_palloc
	if (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
{        //步骤1
   if (large->alloc == NULL) //找到空闲large
    {         
           large->alloc = p;        //将大内存链入该空闲large
            return 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,步骤3

large->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.coloradmin.cn/o/1114027.html

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

相关文章

【Ant Design Table + React】表格列伸缩实现

需求背景&#xff1a;需要实现Antd Table 组件的列伸缩&#xff0c;宽度可以拖拽 在Antd 3.x 的版本中是保留的列伸缩的Demo例子的&#xff1a; 借助 react-resizable 可以实现伸缩列。 # npm 安装 npm install react-resizable --save# yarn 安装 yarn add react-resizable参…

字符函数和字符串函数(详解大全)

重点 C语言中对字符和字符串的处理很是频繁&#xff0c;但是C语言本身是没有字符串类型的&#xff0c;字符串通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数. 1.函数介绍 1.1strlen size_t strlen ( const char * str ); ⭐字符…

Linux——centos7.4磁盘空间调整分配

安装centos7.4操作系统时&#xff0c;采用默认安装方式&#xff0c;导致磁盘分配不太合理&#xff0c;于是重新进行磁盘空间分配。 1、cnetos7.4默认安装完成时磁盘分配情况 可以看到/dev/mapper/centos-home分区占用大部分空间&#xff0c;如今想将根目录空间增大。 注意&…

pandas写入MySQL

安装好pandas、mysql pip install pandas pip install pymysql 导入pandas、mysql import pymysql as mysql import pandas as pd 建立连接 conmysql.connect(host10.10.0.221,userroot,passwordroot,databasepandas,port3306,charsetutf8) 创建游标 curcon.cursor() 读…

R语言处理数量很大(千万级及以上)的数据时的拆分策略-案例一

主要是用R中的groupb_by和 summarise分组查询和统计数据量

碰到it运维故障怎么办丫?突发IT事故怎么快速解决?

随着信息技术的快速发展&#xff0c;企业对于IT系统的依赖程度越来越高。但IT系统突发事件的风险也在不断增加&#xff0c;例如突发故障&#xff0c;例如数据泄露、例如数据入侵等等。那碰到这种it运维故障怎么办&#xff1f;突发IT事故怎么快速解决&#xff1f; 碰到it运维故障…

广度优先搜索(Breadth First Search, BFS)算法

广度优先搜索(Breadth First Search, BFS) 广度优先搜索是一种盲目搜索算法&#xff0c;它认为所有状态(或者说结点)都是等价的&#xff0c;不存在优劣之分。 假如我们把所有需要搜索的状态组成一棵树来看&#xff0c;广搜就是一层搜完再搜下一层&#xff0c;直到找出目标结点…

旅游业媒体套餐7个诀窍助你轻松实现销售目标-华媒舍

旅游业是一个竞争激烈的行业&#xff0c;成功营销对于吸引客户和实现销售目标至关重要。借助媒体资源是一种有效的方式。本文将介绍7个诀窍&#xff0c;借助旅游业媒体套餐轻松实现销售目标。 1. 策划细致的新闻稿 新闻稿是介绍旅游产品和服务的重要工具。确保新闻稿中包含吸引…

【精选】2023网络安全学习路线 非常详细 推荐学习

关键词&#xff1a;网络安全入门、渗透测试学习、零基础学安全、网络安全学习路线 分享2套零基础、进阶学习网络安全/渗透测试教程 第一套是Web安全学习笔记 该笔记详细介绍了计算机网络协议、信息收集、常见漏洞、内网渗透、御用技术等等&#xff0c;全文全面成体系&#x…

京东店铺所有商品数据接口及店铺商品数据分析

获取京东店铺所有商品数据的接口是京东开放平台提供的接口&#xff0c;通过该接口可以获取店铺所有商品数据。 通过京东开放平台接口获取店铺所有商品数据的方法如下&#xff1a; 在开放平台注册成为开发者并创建一个应用&#xff0c;获取到所需的 App Key 和 App Secret 等信…

PHP 基础/练习

练习 成绩定级 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>成绩定级脚本</title> </…

Windows 事件日志监控

Windows 事件日志是记录 Microsoft 系统上发生的所有活动的文件&#xff0c;在 Windows 环境中&#xff0c;将记录系统上托管的系统、安全性和应用程序的事件&#xff0c;事件日志提供包含有关事件的详细信息&#xff0c;包括日期、时间、事件 ID、源、事件类型和发起它的用户。…

spring cloud Eureka集群模式搭建(IDEA中运行)

spring cloud Eureka集群模式搭建&#xff08;IDEA中运行&#xff09; 新建springboot 工程工程整体目录配置文件IDEA中部署以jar包形式启动总结 新建springboot 工程 新建一个springboot 工程&#xff0c;命名为&#xff1a;eureka_server。 其中pom.xml文件为&#xff1a; …

el-input无法输入的问题和表单验证失败问题(亲测有效)-开发bug总结4

大部分无法输入的问题&#xff1a;基本都是没有进行v-model双向数据绑定&#xff0c;这个很好解决。 本人项目中遇到的bug问题如下&#xff1a; 点击添加&#xff0c;表单内可输入用户名 和 用户姓名&#xff0c;但有时会偶发出现无法这两个input框里面无法输入内容。 原因&a…

使用 Rust 开发:以太坊与 Layer2 生态建设新趋势

Rust 是一种系统编程语言&#xff0c;以其出色的性能、内存安全和并发性而闻名&#xff0c;在区块链和 Web3 中广泛应用&#xff0c;大多数编程语言和开发框架都以 Rust 为核心&#xff0c;如 Polkadot、Solana、NEAR、Elrond&#xff08;现名 MultiversX&#xff09;、Hyperle…

探索房产小程序源码中的十大必备功能

作为房地产行业的专家&#xff0c;我们深知房产小程序在实现高效交易和提升用户体验方面的重要性。选择适合的房产小程序源码&#xff0c;关乎着您的平台能否脱颖而出。在本文中&#xff0c;我将为您揭示房产小程序中不可或缺的十大必备功能&#xff0c;助您确保您的小程序具备…

CSDN 使用体验:一路向上

使用CSDN已经很多年了&#xff0c;这些年我真的在这一平台上收获了很多&#xff0c;而这些年CSDN自身也在不断进步&#xff0c;一步步成长&#xff0c;尤其是在最近的使用过程中&#xff0c;我感受到了CSDN前所未有的包容性、全面性&#xff0c;并且收获成就感。 包容性 随着…

Linux 下 Java 安装字体方法

因上线访问图字体乱码了&#xff0c;因为在windows下设置的微软雅黑&#xff0c;linux默认是没有的&#xff0c;所以需要给jdk安装一个微软雅黑字体。按照步骤来&#xff0c;so easy&#xff01; 1&#xff09;首先找到windows下面的字体&#xff0c;不用去其他地方下了&#…

干货:电视机顶盒哪个牌子好?双11必看网络机顶盒排行榜

电视机顶盒是我们必不可少的&#xff0c;新手们在面对众多品牌和产品是难免疑惑电视机顶盒哪个牌子好&#xff0c;双十一买哪款电视机顶盒最实惠&#xff1f;我将给大家分享的是业内公认的网络机顶盒排行榜&#xff0c;想知道哪些电视盒子品牌最值得买&#xff0c;看这篇就足够…

【vue】使用less报错:显示this.getOptions is not a function

在vue-cli中使用 lang“less” 时报错&#xff1a; Module build failed: TypeError: this.getOptions is not a function at Object.lessLoader 原因&#xff1a;版本过高所致&#xff0c;所用版本为 解决&#xff1a;降低版本&#xff1a;npm install less-loader4.1.0 --s…