初识nginx——内存池篇

  为了自身使用的方便,Nginx封装了很多有用的数据结构,比如ngx_str_t ,ngx_array_t, ngx_pool_t 等等,对于内存池,nginx设计的十分精炼,值得我们学习,本文介绍内存池基本知识,nginx内存池的结构和关键代码,并用一个实际的代码例子作了进一步的讲解

 

一、内存池概述

    内存池是在真正使用内存之前,预先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够用时,再继续申请新的内存。

   内存池的好处有减少向系统申请和释放内存的时间开销,解决内存频繁分配产生的碎片,提示程序性能,减少程序员在编写代码中对内存的关注等

   目前一些常见的内存池实现方案有STL中的内存分配区,boost中的object_pool,nginx中的ngx_pool_t,google的开源项目TCMalloc等

二、nginx内存池综述

     nginx为每一个层级都会创建一个内存池,进行内存的管理,比如一个模板,tcp连接,http请求等,在对应的生命周期结束的时候会摧毁整个内存池,把分配的内存一次性归还给操作系统。

     在分配的内存上,nginx有小块内存和大块内存的概念,小块内存 nginx在分配的时候会尝试在当前的内存池节点中分配,而大块内存会调用系统函数malloc向操作系统申请

     在释放内存的时候,nginx没有专门提供针对释放小块内存的函数,小块内存会在ngx_destory_pool 和 ngx_reset_pool的时候一并释放

     区分小块内存和大块内存的原因有2个,

     1、针对大块内存  如果它的生命周期远远短于所属的内存池,那么提供一个单独的释放函数是十分有意义的,但不区分大块内存和小块内存,针对大的内存块 便会无法提前释放了

     2、大块内存与小块内存的界限是一页内存(p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL,NGX_MAX_ALLOC_FROM_POOL的值通过调用getpagesize()获得),大于一页的内存在物理上不一定是连续的,所以如果分配的内存大于一页的话,从内存池中使用,和向操作系统重新申请效率差不多是等价的

      nginx内存池提供的函数主要有以下几个

     

NewImage

三、nginx内存池详解

    nginx使用了ngx_pool_s用于表示整个内存池对象,ngx_pool_data_t表示单个内存池节点的分配信息,ngx_pool_large_s表示大块内存

它们的结构和含义如下

struct ngx_pool_large_s {
ngx_pool_large_t *next;
void *alloc;
};

next:   指向下一个大块内存

alloc:指向分配的大块内存

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

d:内存池的节点的数据分配情况

max:      单个内存池节点容量的最大值

current: 指向当前的内存池节点

chain: 指向一个ngx_chain_t结构

large:  指向大块内存链表

cleanup:释放内存池的callback

log:     用于输出日志

typedef struct {
   u_char *last;
   u_char *end;
   ngx_pool_t *next;
   ngx_uint_t failed;
} ngx_pool_data_t;

last:    内存池节点已分配的末位地址,下一次分配会尝试从此开始

end: 内存池节点的结束位置

next:next指向下一个内存池节点

failed: 当前内存池节点分配失败次数

NewImage

       nginx 内存池示意图1

    在分配内存的时候,nginx会判断当前要分配的内存是小块内存还是大块内存,大块内存调用ngx_palloc_large进行分配,小块内存nginx先会尝试从内存池的当前节点(p->current)中分配,如果内存池当前节点的剩余空间不足,nginx会调用ngx_palloc_block新创建一个内存池节点并从中分配,

如果内存池当前节点的分配失败次数已经大于等于6次(p->d.failed++ > 4),则将当前内存池节点前移一个

(这里有个需要注意的地方,当当前内存节点的剩余空间不够分配时,nginx会重新创建一个ngx_pool_t对象,并且将pool.d->next指向新的ngx_pool_t,新分配的ngx_pool_t对象只用到了ngx_pool_data_t区域,并没有头部信息,头部信息部分已经被当做内存分配区域了)

NewImage

                 nginx 内存池示意图2(新建了一个内存池节点和分配了2个大块内存,其中一个已经释放) 

关键代码

创建内存池代码

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);//间接调用了posix_memalign分配内存

    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;//由于当前内存池只有一个节点所以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;

}

ngx_palloc分配函数代码

void *

ngx_palloc(ngx_pool_t *pool, size_t size)

{

    u_char *m;

    ngx_pool_t *p;

    if (size <= pool->max) //判断是小块内存 还是大块内存

    {

        p = pool->current;

        do {

            m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);

            if ((size_t) (p->d.end - m) >= size) {//尝试在已有的内存池节点中分配内存

                p->d.last = m + size;

                return m;

            }

            p = p->d.next;

        while (p);

        return ngx_palloc_block(pool, size);//当前已有节点都分配失败,创建一个新的内存池节点

    }

    return ngx_palloc_large(pool, size);//分配大块内存

}

消耗内存池

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);//调用需要在内存池释放时同步调用的方法

        }

    }

    for (l = pool->large; l; l = l->next) {//释放大块内存

        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);

        if (l->alloc) {

           ngx_free(l->alloc);

        }

    }

    #if (NGX_DEBUG)

    /*

    * we could allocate the pool->log from this pool

    * so we cannot use this log while free()ing the pool

    */

    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 (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {

        ngx_free(p);//间接调用free释放内存

        if (n == NULL) {

            break;

        }

    }

}

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

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

相关文章

4G微型RTU如何实现冬季工业管网远程监测

随着我国北方全面进入到冬季&#xff0c;多日以来严寒、降雪天气频发&#xff0c;工业基础设施也迎来冬季考验。对于一些输送化工原料、油气和给排水等用途的工业管网设施&#xff0c;在面临极端冰雪天气时易产生各种风险&#xff0c;诸如管道水/气泄漏损耗、低温冻裂、积雪压塌…

linux 中 C++的环境搭建以及测试工具的简单介绍

文章目录 makefleCMakegdb调试 与 coredumpValgrind 内存检测gtest 单元测试 makefile 介绍 安装 : sudo apt install make makefile 的规则: 举例说明 包括&#xff1a;目标文件 、 依赖文件 、 生成规则 使用 &#xff1a; make make clean CMake : CMake是一个…

配置自定义RedisTemplate 解决redis序列化java8 LocalDateTime

目录 配置自定义RedisTemplate 引入依赖 配置连接redis 编写测试类 出现问题 配置序列化 解决redis序列化java8 LocalDateTime 问题背景 问题描述 问题分析 解决方案一&#xff08;全局&#xff09; 解决方案二&#xff08;单个字段&#xff09; 配置自定义RedisTe…

Ubuntu环境下使用Livox mid 360

参考文章&#xff1a; Ubuntu 20.04使用Livox mid 360 测试 FAST_LIO-CSDN博客 一&#xff1a;Livox mid 360驱动安装与测试 前言&#xff1a; Livox mid360需要使用Livox-SDK2&#xff0c;而非Livox-SDK&#xff0c;以及对应的livox_ros_driver2 。并需要修改FAST_LIO中部…

【开源工程及源码】超级经典开源项目实景三维数字孪生智慧港口

智慧港口可视化平台&#xff0c;旨在实现对港口运营的全面监测、智能管理和优化决策。飞渡科技利用数字化、模拟和仿真的技术&#xff0c;通过互联的传感器和设备&#xff0c;实现实时数据的采集、传输和分析&#xff0c;将港口内外的复杂数据以直观、易懂的方式呈现&#xff0…

console.log自定义样式

console.log(%c---------------- HTTP-Success&#xff1a;----------------%c,background: #00cc09; color: #fff; border-radius: 3px 0 0 3px;padding:2px 5px,background: #1475B2; color: #fff; border-radius: 0 3px 3px 0;padding:2px 5px,res.data,)

到底需要会那些技能?才算一个5年经验合格的软件测试工程师

一&#xff1a;经历讲解 微软外包自动化测试两年&#xff0c;而后转入互联网公司做移动端自动化测试一年&#xff0c;经历了入行时的迷茫&#xff0c;而后的笃定&#xff0c;转入移动后对自身定位和价值的怀疑&#xff0c;继而对自动化测试的重新认识&#xff0c;职场三年&…

python调用GPT API

每次让gpt给我生成一个调用api的程序时&#xff0c;他经常会调用以前的一些api的方法&#xff0c;导致我的程序运行错误&#xff0c;所以这期记录一下使用新的方法区调用api 参考网址 Migration Guide&#xff0c;这里简要地概括了一下新版本做了哪些更改 OpenAI Python API l…

nmap端口扫描工具安装和使用方法

nmap&#xff08;Network Mapper&#xff09;是一款开源免费的针对大型网络的端口扫描工具&#xff0c;nmap可以检测目标主机是否在线、主机端口开放情况、检测主机运行的服务类型及版本信息、检测操作系统与设备类型等信息。本文主要介绍nmap工具安装和基本使用方法。 nmap主…

世微 AP5101C高压线性LED恒流驱动芯片 6-100V 2A LED灯电源驱动

产品描述 AP5101C 是一款高压线性 LED 恒流 芯片 &#xff0c; 简单 、 内置功率管 &#xff0c; 适用于 6- 100V 输入的高精度降压 LED 恒流驱动 芯片。电流2.0A。 AP5101C 可实现内置MOS 做 2.0A, 外置MOS 可做 3.0A 的。 AP5101C 内置温度保护功能 &#xff0c;温度保 护点…

如何选择适合的UI自动化测试工具

随着软件开发项目的复杂性增加&#xff0c;UI自动化测试成为确保应用程序质量的关键步骤之一。然而&#xff0c;在选择UI自动化测试工具时&#xff0c;开发团队需要考虑多个因素&#xff0c;以确保选取的工具适用于项目需求并提供可靠的测试结果。 1. 了解项目需求 在选择UI自动…

C语言中关于操作符的理解

本篇文章只会列出大家在生活中经常使用的操作符 算术操作符 在算数操作符中常用的有&#xff0c;&#xff0c;-&#xff0c;*&#xff0c;/&#xff0c;% &#xff0c;我们重点讲一讲 / (除) 和 % (模) " / "运算 #include <stdio.h>int main() {int a5/2;fl…