基本结构

1. ngx_pool_t

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

提供的函数

1. ngx_create_pool(): 分配并初始化一块 size 大小的内存池

ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log)
{
ngx_pool_t *p; /* 进行16字节的内存对齐分配 */
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);
/* max的最大值为4095,假设一页大小为4KB.
* 问:为什么要将pool->max字段的最大值限制在一页内存?
* 这个字段是区分小块内存和大块内存的临界,所以这里的原因也就在于只有当
* 分配的内存空间小于一页时才有缓存的必要(即向Nginx内存池申请),否则的
* 话,还不如直接利用系统接口malloc()向操作系统申请。
*/
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_pool_t 占去开头一部分(即额外的开销 overhead),Nginx实际是从该内存池里 p->d.last 指向的

起始位置开始分配给用户的。

2. ngx_palloc(): 从内存池pool中分配size大小的内存

void *ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max)
{
/* 第 3 个参数为 1 表示需要进行内存对齐 */
return ngx_palloc_small(pool, size, 1);
}
#endif return ngx_palloc_large(pool, size);
}

该函数尝试从 pool 内存池里分配 size 大小的内存空间。有两种情况:

  1. 如果 size 小于等于 pool->max (称之为小块内存分配),即小于等于内存池总大小或 1 页内存(4K - 1),则调用

    ngx_palloc_small() 进行分配;
  2. 否则为大块内存分配, 即调用 ngx_palloc_large() 进行分配。

2.1 ngx_palloc_small():小块内存分配

static ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
u_char *m;
ngx_pool_t *p; /* current 指向当前正在使用的 ngx_pool_t 内存池首地址 */
p = pool->current; do
{
m = p->d.last; /* 进行内存对齐 */
if (align)
{
m = ngx_align_ptr(m, NGX_ALIGNMENT);
} /* 若当前内存池的可用空间充足,则直接从当前内存池中分配 size 大小空间 */
if ((size_t) (p->d.end - m) >= size)
{
p->d.last = m + size; return m;
} /* 若当前内存池的可用空间不足 size,则指向下一个内存池节点 */
p = p->d.next; } while (p); /* 若遍历完 p->d.next 所链接的内存池链表都没有足够的内存空间,则调用 ngx_palloc_block()
* 再分配一个等同大小的内存池,并将其链接到 p->d.next 内存池链表中 */
return ngx_palloc_block(pool, size);
}

ngx_palloc_block(): 分配新的内存池并链接到内存池链表尾部

static void *ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new; /* 获取 pool 指向的内存池总大小,其中包括内存池开头占用的 sizeof(ngx_pool_t) 大小的 overhead */
psize = (size_t) (pool->d.end - (u_char *) pool); /* 分配 psize 大小并 16 字节对齐的内存空间 */
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
if (m == NULL)
{
return NULL;
} /* ngx_pool_t 类型的结构体指针 new 指向该新分配的内存起始地址 */
new = (ngx_pool_t *) m; new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = 0; /* 该新分配的内存池的起始为 sizeof(ngx_pool_data_t) 大小的 overhead */
m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
/* 表示从新内存池中分配 size 大小的内存空间 */
new->d.last = m + size; /* 遍历 pool->current 指向的内存池链表,根据 p->d.failed 值更新 pool->current */
for (p = pool->current; p->d.next; p = p->d.next)
{
/* current 字段的变动是根据统计来做的,如果从当前内存池节点分配内存总失败次数(记录在
* 字段 p->d.failed 内) 大于等于 6 次(这是一个经验值,具体判断是 "if (p->d.failed++ > 4)"
* 由于 p->d.failed 初始值为 0,所以当这个判断为真时,至少已经分配失败 6 次了),就将
* current 字段移到下一个内存池节点,如果下一个内存池节点的 failed 统计数也大于等于 6 次,
* 再下一个,依次类推,如果直到最后仍然是 failed 统计数大于等于 6 次,那么 current 字段
* 指向刚新分配的内存池节点 */
if (p->d.failed++ > 4)
{
pool->current = p->d.next;
}
} /* 将新分配的内存池节点添加到内存池链表的尾部 */
p->d.next = new; return m;
}
申请并链接新的内存池节点图

2.2 ngx_palloc_large(): 大块内存分配

static void *ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
void *p;
ngx_uint_t n;
ngx_pool_large_t *large; /* 直接malloc size 大小的内存 */
p = ngx_alloc(size, pool->log);
if (p == NULL)
{
return NULL;
} n = 0; /* 遍历 large 指向的大块内存链表 */
for (large = pool->large; large; large = large->next)
{
/* 在内存池的使用过程中,由于大块内存可能会被释放(通过函数 ngx_pfree()),
* 此时将空出其对应的头结构体变量 ngx_pool_large_t,所以在进行实际的链
* 头插入操作前,会去搜索当前是否有这种情况存在。如果有,则直接将新分配的
* 内存块设置在其 alloc 指针字段下。又如下可知,这种搜索也只是对前面 5 个
* 链表节点进行 */
if (large->alloc == NULL)
{
large->alloc = p;
return p;
} /* 这里表示仅搜索大块内存链表中前面的 5 个节点 */
if (n++ > 3)
{
break;
}
} /* 若 ngx_pool_large_t 类型的指针 large 开始时为 NULL,则从内存池 pool 中
* 分配一块 sizeof(ngx_pool_large_t) 大小的内存,并使 large 指向该内存起始 */
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL)
{
ngx_free(p);
return NULL;
} /* alloc 指向上面直接 malloc 的大块内存 */
large->alloc = p;
/* 将该新分配的大块内存节点 large 插入到 pool->large 指向的大块内存链表头部 */
large->next = pool->large;
pool->large = large; return p;
}
申请第一个大块内存

继续申请大块内存

释放大块内存

3. ngx_pnalloc()

该函数与 ngx_palloc() 类似,只不过是没有进行内存对齐.

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);
}
#endif return ngx_palloc_large(pool, size);
}

4. ngx_pmemalign()

void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
{
void *p;
ngx_pool_large_t *large; /* 分配一块 size 大小并进行 alignment 字节对齐的内存空间 */
p = ngx_memalign(alignment, size, pool->log);
if (p == NULL)
{
return NULL;
} /* 从内存池 pool 中分配一块 sizeof(ngx_pool_large_t) 大小的内存空间 */
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL)
{
ngx_free(p);
return NULL;
} /* 将 p 指向的新分配的大块内存挂到 large->alloc 下 */
large->alloc = p;
/* 然后将 large 添加到 pool->large 指向的大块内存链表头 */
large->next = pool->large;
pool->large = large; return p;
}

5. ngx_pfree()

ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p)
{
ngx_pool_large_t *l; /* 遍历 pool->large 指向的大块内存链表,在该链表中找到 p 指向的大块内存,
* 然后释放 p 指向的内存空间,并将 alloc 置为 NULL*/
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;
}
} /* 没有找到则返回 NGX_DECLINED */
return NGX_DECLINED;
}

6. 其他函数

Nginx 此外提供的一套函数接口 ngx_pool_cleanup_add()、ngx_pool_run_cleanup_file()、ngx_pool_cleanup_file()、

ngx_pool_delete_file() 用于对内存与其他资源的关联管理,也就是说,从内存池里申请一块内存时,可能外部会

附加一些其他资源(比如打开的文件),这些资源的使用和申请的内存是绑定在一起的,那么在进行资源释放的时候,

希望这些资源的释放能和内存池释放一起进行(通过 handler() 回调函数),既能避免无意的资源泄漏,又省得单独

执行资源释放的麻烦。

Nginx数据结构之内存池的更多相关文章

  1. Nginx系列三 内存池的设计

    Nginx的高性能的是用非常多细节来保证,epoll下的多路io异步通知.阶段细分化的异步事件驱动,那么在内存管理这一块也是用了非常大心血.上一篇我们讲到了slab分配器,我们能够能够看到那是对共享内 ...

  2. nginx源码学习----内存池

    最近在进行监控平台的设计,之前一直觉得C/C++中最棘手的部分是内存的管理上,远不止new/delete.malloc/free这么简单.随着代码量的递增,程序结构复杂度的提高.各种内存方面的问题悄然 ...

  3. nginx源码分析—内存池结构ngx_pool_t及内存管理

    Content 0. 序 1. 内存池结构 1.1 ngx_pool_t结构 1.2 其他相关结构 1.3 ngx_pool_t的逻辑结构 2. 内存池操作 2.1 创建内存池 2.2 销毁内存池 2 ...

  4. Nginx 之 内存池

    1.基本结构 先来学习一下nginx内存池的几个主要数据结构:[见:./src/core/ngx_palloc.h/.c]     ngx_pool_data_t(内存池数据块结构) 1: typed ...

  5. 菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t

    菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

  6. 菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t[转]

    菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...

  7. linux内存源码分析 - 内存池

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 内存池是用于预先申请一些内存用于备用,当系统内存不足无法从伙伴系统和slab中获取内存时,会从内存池中获取预留的 ...

  8. 简单内存池的C实现

    1. 序言 对于程序开发人员来说,会经常听到这种"池"的概念,例如"进程池","线程池","内存池"等,虽然很多时没有吃 ...

  9. 初识nginx——内存池篇

    初识nginx——内存池篇 为了自身使用的方便,Nginx封装了很多有用的数据结构,比如ngx_str_t ,ngx_array_t, ngx_pool_t 等等,对于内存池,nginx设计的十分精炼 ...

随机推荐

  1. elment-ui表单验证

    <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-widt ...

  2. 如何检测浏览器是否能用ActiveX

    var xhr=false; function CreateXHR(){ try{ //检查能否用activexobject xhr=new ActiveXObject("msxml2.XM ...

  3. LEANGOO卡片

    转自:https://www.leangoo.com/leangoo_guide/leangoo_cards.html#toggle-id-10 Leangoo的卡片可以是需求.目标.任务.问题.缺陷 ...

  4. ListSetAndMap

    package com.collection.test; import java.util.ArrayList; import java.util.HashMap; import java.util. ...

  5. web开发:jquery高级

    一.jq选择器 二.属性操作 三.jq轮播图 四.样式操作 五.jq动事件 六.jq动画 七.自定义动画 八.jq动画案例 一.jq选择器 - css3语法选择器 ```js$('css3选择器位') ...

  6. 010.简单查询、分组统计查询、多表连接查询(sql实例)

    -------------------------------------day3------------ --添加多行数据:------INSERT [INTO] 表名 [(列的列表)] --SEL ...

  7. 最小m子段和(动态规划)

    问题描述: 给定n个整数组成的序列,现在要求将序列分割为m段,每段子序列中的数在原序列中连续排列.如何分割才能使这m段子序列的和的最大值达到最小? 输入格式: 第一行给出n,m,表示有n个数分成m段, ...

  8. linux和unix下crontab的使用

    在LINUX中,周期执行的任务一般由cron这个守护进程来处理 [ps -ef | grep cron].cron读取一个或多个配置文件,这些配置文件中包含了命令行及其调用时间.cron的配置文件称为 ...

  9. SpiderMan成长记(爬虫之路)

    第一章 爬虫基础 1.1 爬虫基本原理 1.2 请求库 -- urllib库的使用 1.3 请求库 -- requests库的使用 1.4 数据解析 -- 正则基础 1.5 数据解析 -- lxml与 ...

  10. new一个对象的过程

    不用死记硬背,理解才是硬道理.只需要写个例子,然后输出看一下就清楚了 首先我们看下new Person输出什么? var Person = function(name, age) { this.nam ...