参考:

http://www.cnblogs.com/jzhlin/archive/2012/06/07/ngx_palloc.html

上一篇已经通过对 ngx_palloc这个内存池(pool)管理的核心函数--内存分配函数进行解析,我们窥探到了Nginx内存管理的主体方法还有对于大内存需求的解决之道,同时也对管理内存池的数据结构有了更深一步的认识,通过这些认识我们可以得到以下这样一张数据结构的示意图:

图3 Nginx内存管理数据结构示意图

  做说明下,这里示意的是有需求大内存分配时的结构示意图,为了图示的方便,我们将 large_t特殊话到了和 large所在的同一个pool单元里面,其实实际运行中并非一定在同一个pool单元中。如果没有大内存需求时 large_t 也并不存在。

分析完了,内存分配函数 ngx_palloc,可以说nginx内存分配机制的解析我们已经完成了一半,对于 ngx_palloc的变种函数如:ngx_pcalloc、ngx_pnalloc等就不一一详细说明了,和 ngx_palloc结构类似或者只是添加了少许功能(比如,初始化内存数据为0等)。我们再来分析内存池的其他函数

1、创建内存池

ngx_pool_t*ngx_create_pool(size_t size, ngx_log_t *log)

此函数结构简明,是对 ngx_pool_t按照 size的大小进行些必要的初始化,并返回一个指针。

2、释放内存池

内存池pool的注销函数 ngx_destroy_pool

  分析这个函数,首先得分析Nginx内存池的注销机制。我知道在需求内存时很可能用到一些数据结构,注销时需要特别的对它进行释放,否则可能发生内存泄漏。对这样的情况,Nginx内存管理中,采用了一个有特点的结构体来应对--ngx_pool_cleanup_t,对应的就是pool单元中的 cleanup。

struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;
    void                 *data;
    ngx_pool_cleanup_t   *next;
};

  一个看显然就明白是个链表,不过,ngx_pool_cleanup_pt是什么呢?请看下面这个定义

typedef void (*ngx_pool_cleanup_pt)(void *data);

 很明白了,原来是一个函数指针。这一下问题就显然解决了,在内存管理中遇到这样的特殊的数据结构,只要编写它相应的释放函数,用函数指针加载到pool单元上,在注销pool内存池时调用即可。特别说明下是,ngx_pool_cleanup_t 数据结构中的data存储的是 handler 指向的函数可能需要用到的参数。看起来很有C++中类的析构函数的味道,可以有多个这样的函数用 ngx_pool_cleanup_t链表的形式保存。回到,Nginx内存管理的源代码里,我们可以找到 ngx_pool_cleanup_add这样的一个函数,和预想的一样,它就是分配释放函数的内存,并返回地址。附上源码:

ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t*p, size_t size)

#if (NGX_PCHECK)
ngx_pool_cleanup_t *
_ngx_pool_cleanup_add(ngx_pool_t *p, size_t size,
char *name, char *file, char *func, ngx_int_t line)
#else
ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
#endif
{
    ngx_pool_cleanup_t  *c;

#if (NGX_PCHECK)
    c = _ngx_palloc(p, sizeof(ngx_pool_cleanup_t),
name, file, func, line);
#else
    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
#endif
if (c == NULL) {
        return NULL;
    }

if (size) {
#if (NGX_PCHECK)
c->data = _ngx_palloc(p, size, name, file, func, line);
#else
        c->data = ngx_palloc(p, size);
#endif
        if (c->data == NULL) {
            return NULL;
        }

} else {
        c->data = NULL;
    }

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

好了,理解了释放特殊结构体后,我们再来看 ngx_destroy_pool就比较简单了。

内存池的建设过程,先建pool单元,如果有大内存需求再建large。

内存池注销正好相反,首先处理 ngx_pool_cleanup_t 的特殊需求,如果有的话然后释放分配large,最后释放pool。其中如果配置了要写的DEBUG日志的,就还对DEBUG日志进行记录。附上源码:

void

ngx_destroy_pool(ngx_pool_t *pool)

{

ngx_pool_t          *p, *n;

ngx_pool_large_t    *l;

ngx_pool_cleanup_t  *c;

/*

    处理 ngx_pool_cleanup_t的特殊需求

*/

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

        }

    }

/*

  释放分配large

*/

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 couldallocate the pool->log from this pool

* so we cannot usethis 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

/*

释放pool

*/

for (p = pool, n =pool->d.next; /* void */; p = n, n = n->d.next) {

ngx_free(p);

if (n == NULL){

break;

}

}

}

大家或许有点疑惑,内存真的没有泄漏了么?ngx_pool_cleanup_t 所占的内存,释放了么?答案当然是肯定的!如果有这样的疑惑,请仔细看 ngx_pool_cleanup_add函数他给 ngx_pool_cleanup_t 分配的内存空间也是pool单元中的内存(同样的 ngx_pool_large_t ),释放pool的时候一并释放出来。

  分析到这里,Nginx内存管理机制源码的分析就基本上结束了。感慨到Nginx内存池的设计确实精美,pool单元数据结构的设计(实用)、内存分配机制(大内存特殊处理,大内存信息的结构体也在内存池中)、内存释放机制(经典的函数指针用法,很巧妙的防止内存泄漏),很多地方都可以借鉴!

3、worker进程退出时候会释放cycle->pool

通过函数ngx_worker_process_cycle可以看出,由于进入for(;;)之前执行了ngx_worker_process_init(cycle,worker)即会调用每个模块的init_process指向的函数,退出for(;;)后执行ngx_worker_process_exit(cycle)即会调用ngx_destroy_pool(cycle->pool);

我们可以在每个模块的init_process指向的函数中从cycle->pool中申请内存并且调用cln = ngx_pool_cleanup_add(pool,0)设置清理函数cln->handler,当worker进程退出时候释放cycle->pool之前则会调用每个模块注册的cln->handler。

4、实验

我们在  ngx_palloc添加上一条 printf("call ngx_palloc\n")其他函数也添加上类似的语句,编写如下main代码:

void fun(void *p)
{
    printf("call fun\n");
}
int ngx_cdecl
main(int argc, char *const *argv)
{
 
    ngx_pool_t    *t;
    ngx_pool_cleanup_t *clt;
 
    void *p1,*p2,*p3;
 
    t=ngx_create_pool(512,NULL); /*创建pool*/
    printf("\n");
    p1=ngx_palloc(t,416); /*分配内存,在可分配内*/
    printf("\n");
    p2=ngx_palloc(t,216);/*分配内存,在可分配内,但pool中没有足够的内存空间*/
    printf("\n");
    p3=ngx_palloc(t,624);/*分配大内存*/
    printf("\n");
    clt=ngx_pool_cleanup_add(t, sizeof(void *));/*添加释放特殊结构体的函数*/
    clt->handler=&fun;
    ngx_destroy_pool(t);/*pool池注销*/
    printf("\n");
    return 0;
}

实验结果,截图如下:

图4 Nginx内存管理机制实验截图

实验结果简单说明下:

call ngx_create_pool //创建了pool池

call ngx_palloc  //分配内存,在可分配内

call ngx_palloc //分配内存,在可分配内,但pool中没有足够的内存空间

call ngx_palloc_block //分配新的pool单元

callngx_palloc  //分配大内存
call ngx_palloc_large  //调用大内存分配函数
call ngx_palloc  //分配 ngx_pool_large_t结构体空间

call ngx_pool_cleanup_add //添加释放特殊结构体的函数

callngx_palloc  //分配ngx_pool_cleanup_t 的空间
call ngx_palloc  //分配函数参数的空间
call ngx_destroy_pool  //内存池pool池注销

call fun  //调用释放特殊结构体的函数释放

以上实验结果,完全符合我们分析Nginx内存分配机制,Nginx内存池(pool)的分析到此处就结束了。经典的内存池管理机制!

ngx_pool_cleanup_add使用-原理相关推荐

  1. UUID的使用及其原理

    今天敲项目要用UUID,想起之前老师告诉UUID的使用,但没说具体的生成逻辑,于是我进行了百度 首先,UUID的使用: //生成随机的UUID String uuid = UUID.randomUUI ...

  2. etcd 笔记(01)— etcd 简介、特点、应用场景、常用术语、分布式 CAP 理论、分布式原理

    1. etcd 简介 etcd 官网定义: A highly-available key value store for shared configuration and service discov ...

  3. git原理及常见使用方法

    Git 原理入门-来自阮一峰 Git 是最流行的版本管理工具,也是程序员的必备技能之一. 即使天天使用它,很多人也未必了解它的原理.Git 为什么可以管理版本?git add.git commit这些 ...

  4. 微机原理—定时计数控制接口

    别看题目很高深,其实就是很简单的定时器和计数器而已. 通常用手机定个闹钟,就是定时器的使用. 工厂里通过传送带上安装传感器,传感器传输给计算机的信号用来计数. 这是一些很简单的应用,通过很小的一个芯片 ...

  5. 三层交换机原理:01路由器如何隔离广播域?

    前言: 当网络规模较大的时候,需要设备来隔离广播域,防止网络中因产生广播风暴而导致网络效率降低,而二层交换机不能隔离广播域,所以需要三层路由器设备来隔离广播域! 但三层路由器为什么能够隔离广播域,是如 ...

  6. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

  7. BiLSTM-CRF学习笔记(原理和理解) 维特比

    https://www.zhihu.com/question/20136144 维特比详解 BiLSTM-CRF 被提出用于NER或者词性标注,效果比单纯的CRF或者lstm或者bilstm效果都要好 ...

  8. 【Learning Notes】线性链条件随机场(CRF)原理及实现

    1. 概述 条件随机场(Conditional Random Field, CRF)是概率图模型(Probabilistic Graphical Model)与区分性分类( Discriminativ ...

  9. Jieba分词原理与解析

    1 HMM模型 马尔科夫过程: 以天气判断为例:引出隐马尔科夫模型 于是我们可以将这种类型的过程建模为有一个隐藏的马尔科夫过程和一个与这个隐藏马尔科夫过程概率相关的并且可以观察到的状态集合.这就是本文 ...

最新文章

  1. 图灵4月精彩新书预告
  2. SpringBoot实战(十一)之与JMS简单通信
  3. Dubbo原理和源码解析之服务引用
  4. OpenCV cv :: UMat与DirectX9ex曲面的互操作性的实例(附完整代码)
  5. 一个filter子查询测试
  6. densenet论文_DRCN论文解读
  7. eclipse中的汉字极小的解决方案(转载)
  8. RabbitMQ学习系列(二): RabbitMQ安装与配置
  9. openfire服务器
  10. Basic Calculator II
  11. python语言易错知识点强化
  12. java中计算明年今天的日期_计算今天之后的下一个周年日
  13. php汉字组合算法,php数字转汉字的函数算法
  14. ssm房屋租赁管理系统ssm房屋管理系统JSP网上租房系统JSP房产信息网站房屋租赁系统房屋
  15. 【Java】JDBC基础使用教程
  16. Microsoft Office 2007 Beta 2 下载(含所有的CD-KEY)
  17. 改变linux环境背景色,改变Linux 字体和背景颜色
  18. Java入门第65课——根据周长计算不同形状图形的面积
  19. C++语言程序设计(第4版)郑莉练习
  20. #python 自动识别视频字幕

热门文章

  1. 白炽灯和节能灯哪个更护眼?分享几款护眼的LED灯
  2. linux 格式化磁盘
  3. Linux配置与管理NFS服务器
  4. 【面相也可以看出一个人的精神状况】
  5. npm: browserslist插件 (webpack搭配.browserslistrc筛选符合要求的版本浏览器)
  6. 10个杀手级应用的Python自动化脚本
  7. Python库之math库
  8. 交通运输中的人工智能AI
  9. WinForm 皮肤设置及推荐
  10. 使用html div 画一个简易的房子