nginx源码学习_数据结构(ngx_pool_t)
nginx中关于ngx_pool_t的数据结构位于src/core/ngx_palloc.c和src/core/ngx_palloc.h中,该数据结构主要是和内存池相关的,写下这篇博客前参考了网上很多文章,研究将近3个晚上,下面把网上的文章和自己的理解做一个汇总。
首先先看2个比较重要的结构体:
1、ngx_pool_data_t
typedef struct {
//申请过的内存的尾地址,可申请的首地址
//pool->d.last ~ pool->d.end 中的内存区便是可用数据区。
u_char *last;
//当前内存池节点可以申请的内存的最终位置
u_char *end;
//下一个内存池节点ngx_pool_t,见ngx_palloc_block
ngx_pool_t *next;
//当前节点申请内存失败的次数, 如果发现从当前pool中分配内存失败四次,则使用下一个pool,见ngx_palloc_block
ngx_uint_t failed;
} ngx_pool_data_t;
2、ngx_pool_s
struct ngx_pool_s {
//节点数据
ngx_pool_data_t d;
//当前内存节点可以申请的最大内存空间
size_t max;
//每次从pool中分配内存的时候都是从curren开始遍历pool节点获取内存的,其实就是内存池中可以申请内存的第一个节点
//之所以有节点的概念是因为后面我们会介绍到小块的内存池是通过尾插法维护了一个链表的
ngx_pool_t *current;
//暂时没有研究
ngx_chain_t *chain;
//节点中大内存块指针
ngx_pool_large_t *large;
//暂时没有研究
ngx_pool_cleanup_t *cleanup;
//暂时没有研究
ngx_log_t *log;
};
然后再看几个宏定义:
typedef struct ngx_pool_s ngx_pool_t; typedef struct ngx_chain_s ngx_chain_t; typedef struct ngx_log_s ngx_log_t;
这几个宏位于src/core/ngx_core.h中,就是上面的xxx_s都会被搞成xxx_t,免得看代码的时候对应不上。
再看一下用UML绘制的ngx_pool_t的逻辑结构图:

知道了基本结构体之后,我们就可以看内存池相关的函数了。
1、ngx_create_pool
ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
ngx_pool_t *p;
// 分配一块 size 大小的内存 内存空间16字节对齐
p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
if (p == NULL) {
return NULL;
}
// 对pool中的数据项赋初始值
//可用空间要减去这个头部 首sizeof(ngx_pool_t)便是pool的header信息
//header信息中的各个字段用于管理整个pool
p->d.last = (u_char *) p + sizeof(ngx_pool_t);
//指向当前节点最后面
p->d.end = (u_char *) p + size;
p->d.next = NULL;
p->d.failed = ;
size = size - sizeof(ngx_pool_t);
//不能超过NGX_MAX_ALLOC_FROM_POOL
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;
}
执行了上面的操作之后,内存的图示(下面4个图均表示同样的情况,均参考了网上博客,参考地址本文末尾列出)如下:

图1

图2

图3

图4
2、ngx_palloc
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
u_char *m;
ngx_pool_t *p;
// 判断 size 是否大于 pool 最大可使用内存大小
if (size <= pool->max) {
//从current所在的pool数据节点开始往后遍历寻找哪个节点可以分配size内存
p = pool->current;
do {
// 将 m 对其到内存对齐地址
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
// 判断 pool 中剩余内存是否够用,够用就直接返回【情况1】
if ((size_t) (p->d.end - m) >= size) {
p->d.last = m + size;
return m;
}
//如果当前节点的内存不够,则在下一个节点的内存块中分配空间
p = p->d.next;
} while (p);
//都没有空间的话,就需要重新搞一个节点了,用尾插法插入链表【情况2】
return ngx_palloc_block(pool, size);
}
//判断size已经大于pool->max的大小了,所以直接调用ngx_palloc_large进行大内存分配【情况3】
return ngx_palloc_large(pool, size);
}
在上面内存分配的时候用到了一个宏:
#define ngx_align_ptr(p, a) \
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - )) & ~((uintptr_t) a - ))
对于这个宏参考网上流程如下:




下面还是通过图示来表示上面的流程(所有图示均参考了网上博客,参考地址本文末尾列出)
【情况1】就是在当前的节点下面找,如果可以找到,直接返回地址,并更改对应的last指针(对应下面图1、图2和图3的情况);当前节点下找不到,就去后面的节点找,如果可以找到也返回地址,并更改对应的last指针(对应下面图4和图5的情况)

图1

图2

图3

图4

图5
【情况2】对应现有所有节点下都不足以分配新的内存(该新内存值小于max),调用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 的大小
psize = (size_t) (pool->d.end - (u_char *) pool);
// 在内存对齐了的前提下,新分配一块内存
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
if (m == NULL) {
return NULL;
}
new = (ngx_pool_t *) m;
new->d.end = m + psize;
new->d.next = NULL;
;
//注意,新分配节点的头并不是全部了(ngx_pool_t),而是一部分(ngx_pool_data_t)
m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size;
// 判断在当前 pool 分配内存的失败次数,即:不能复用当前 pool 的次数,
// 如果大于 4 次,这放弃在此 pool 上再次尝试分配内存,以提高效率
//如果失败次数大于4(不等于4),则更新current指针,放弃对老pool的内存进行再使用
for (p = pool->current; p->d.next; p = p->d.next) {
) {
// 更新 current 指针, 每次从pool中分配内存的时候都是从curren开始遍历pool节点获取内存的
pool->current = p->d.next;
}
}
// 让旧指针数据区的 next 指向新分配的 pool,这里就把节点都链接起来了
p->d.next = new;
return m;
}

图1

图2

图3

图4
【情况3】想要分配的内存大于了允许分配的最大内存,那么直接就用malloc()系统函数分配空间,并且也不用对齐了。非常值得注意的一点,我在看的时候纠结了新分配的daneic大内存节点信息保存在哪里?实际上是保存在内存池节点里面的内存块中的。
借用网上资料的解释:注意每块大内存都对应有一个头部结构(next&alloc),这个头部结构是用来将所有大内存串成一个链表用的。这个头部结构不是直接向操作系统申请的,而是当做小块内存(头部结构没几个字节)直接在内存池里申请的。这样的大块内存在使用完后,可能需要第一时间释放,节省内存空间,因此nginx提供了接口函数:ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);此函数专门用来释放某个内存池上的某个大块内存,p就是大内存的地址。ngx_pfree只会释放大内存,不会释放其对应的头部结构,毕竟头部结构是当做小内存在内存池里申请的;遗留下来的头部结构会作下一次申请大内存之用。
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 的新内存
// 注意:此处不使用 ngx_memalign 的原因是,新分配的内存较大,对其也没太大必要
p = ngx_alloc(size, pool->log);
if (p == NULL) {
return NULL;
}
n = ;
// 查找largt链表上空余的large 指针
for (large = pool->large; large; large = large->next) {
//有空余的话,就用这个空余的large,改变一下指针即可,免去再分配内存了
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
// 如果当前 large 后串的 large 内存块数目大于 3 (不等于3),
// 则直接去下一步分配新内存,不再查找了,并且用头插法弄到链表表头
) {
break;
}
}
//在内存池中分配内存,这个内存非常小,仅仅是ngx_pool_large_t的头
large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
if (large == NULL) {
ngx_free(p);
return NULL;
}
// 将新分配的 large 串插入链表头部
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}

图1

图2

图3

图4
概况一下,总体的内存池结构图示如下:

图1

图2

图3
本文参考自:
http://www.cnblogs.com/v-July-v/archive/2011/12/04/2316410.html
http://blog.csdn.net/apelife/article/details/52974336
http://www.linuxidc.com/Linux/2011-08/41860.htm (此博客中图有误,我都重新改过来了)
http://developer.51cto.com/art/201108/283814.htm
http://www.cnblogs.com/xiekeli/archive/2012/10/17/2727432.html
http://blog.zhipcui.com/nginx/2015/01/12/nginx-pool.html
nginx源码学习_数据结构(ngx_pool_t)的更多相关文章
- nginx源码学习_数据结构(ngx_str_t)
nginx中关于字符串的数据结构位于src/core/ngx_string.c和src/core/ngx_string.h中 先来看一下数据结构: typedef struct { size_t le ...
- nginx源码学习_数据结构(ngx_int_t)
nginx中关于整型的数据结构位于src/core/ngx_config.h中 结构比较简单,就是一个typedef的操作,具体如下: typedef intptr_t ngx_int_t; type ...
- nginx源码学习_源码结构
nginx的优秀除了体现在程序结构以及代码风格上,nginx的源码组织也同样简洁明了,目录结构层次结构清晰,值得我们去学习.nginx的源码目录与nginx的模块化以及功能的划分是紧密结合,这也使得我 ...
- nginx源码学习资源(不断更新)
nginx源码学习是一个痛苦又快乐的过程,下面列出了一些nginx的学习资源. 首先要做的当然是下载一份nginx源码,可以从nginx官方网站下载一份最新的. 看了nginx源码,发现这是一份完全没 ...
- nginx源码学习资源
http://www.cnblogs.com/yjf512/archive/2012/06/13/2548515.html nginx源码学习是一个痛苦又快乐的过程,下面列出了一些nginx的学习资源 ...
- 『TensorFlow』SSD源码学习_其一:论文及开源项目文档介绍
一.论文介绍 读论文系列:Object Detection ECCV2016 SSD 一句话概括:SSD就是关于类别的多尺度RPN网络 基本思路: 基础网络后接多层feature map 多层feat ...
- nginx源码学习资源(不断更新)转
原文地址:http://www.cnblogs.com/yjf512/archive/2012/06/13/2548515.html nginx源码学习是一个痛苦又快乐的过程,下面列出了一些nginx ...
- nginx源码学习 资料
首先要做的当然是下载一份nginx源码,可以从nginx官方网站下载一份最新的. 看了nginx源码,发现这是一份完全没有注释,完全没有配置文档的代码. 现在你最希望要的是一份注释版的nginx源码, ...
- 『TensorFlow』SSD源码学习_其四:数据介绍及TFR文件生成
Fork版本项目地址:SSD 一.数据格式介绍 数据文件夹命名为VOC2012,内部有5个子文件夹,如下, 我们的检测任务中使用JPEGImages文件夹和Annotations文件夹. JPEGIm ...
随机推荐
- luogu P4012 深海机器人问题
luogu P4012 深海机器人问题 // luogu-judger-enable-o2 #include<queue> #include<cstdio> #include& ...
- 【DFS序】【莫队算法】【权值分块】bzoj1803 Spoj1487 Query on a tree III
基本等同这个,只是询问的东西不大一样而已. http://www.cnblogs.com/autsky-jadek/p/4159897.html #include<cstdio> #inc ...
- python - ImportError: No module named pywintypes
must restart the python shell to avoid this issue after pywin32 installed
- android加密解密完美教程
经常使用加密算法:DES.3DES.RC4.AES,RSA等; 对称加密:des,3des,aes 非对称加密:rsa 不可逆加密:md5 加密模式:ECB.CBC.CFB.OFB等; 填充模式:No ...
- javascript快速入门14--DOM基础
DOM(Document Object Model)——文档对象模型 什么是DOM? Document Object Model (DOM)是HTML和XML文档的编程接口.它提供了上述文档的一种结构 ...
- 多重采样(MultiSample)下的FBO反锯齿 【转】
在三维渲染的过程中,锯齿总是让人讨厌的东西.抗锯齿的一种采用方式是多重采样,本文主要小记一下FBO与多重采样的关系.——ZwqXin.com 首先,关于FBO(Frame Buffer Object) ...
- 50行代码实现缓存,JAVA内存模型原理
遇见这样的高人怎么办??下面是一个简单缓存的实现,相当牛叉!自己看吧,只有50行代码. 摘自:http://www.oschina.net/code/snippet_55577_3887 import ...
- solr6.6 配置同义词
1.配置managed-schema <fieldType name="text_mmseg4j_simple" class="solr.TextField&quo ...
- Python \xd7\xaa\xd5\xbdOTT TV\xb1\xa6\xbd\xe0 编码
import chardet s = '\xd7\xaa\xd5\xbdOTT TV\xb1\xa6\xbd\xe0\xc7\xa3\xca\xd6\xd2\xf8\xba\xd3\xa1\xa4\x ...
- 警惕rapidxml的陷阱(二):在Android上默认内存池分配数组过大,容易导致栈溢出
上一篇随笔中提到了,rapidxml在每个xml对象中维护了一个内存池,自己管理变量的生存周期.看起来很好,但我们在实际使用中还是出现了问题. 项目中我们的模块很快写好了,在windows和linux ...