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 ...
随机推荐
- [Codeforces 32E] Hide-and-Seek
Brief Intro: 给两个人的坐标,一堵墙和一面镜子,询问两人能否看见对方 Solution: 一道以分类讨论为主的计算几何题, 分别讨论两人坐标连线是否经过墙/镜子即可, 难点在于如何求出点x ...
- [CF403D]Beautiful Pairs of Numbers
题意:给定$n,k$,对于整数对序列$\left(a_1,b_1\right),\cdots,\left(a_k,b_k\right)$,如果$1\leq a_1\leq b_1\lt a_2\leq ...
- Scala零基础教学【61-80】
第61讲:Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析 第62讲:Scala中上下文界定内幕中的隐式参数与隐式参数的实战详解及其在Spark中的应用源码解析 /** ...
- Jetty错误: badMessage: java.lang.IllegalStateException: too much data after closed for HttpChannelOverHttp@472adad9{r=2,c=false,a=IDLE,uri=}
最近用Jetty跑Spring MVC接收POST请求(POST中数据很大).出现数据无法获取到的问题.如: @RequestMapping(value = "/receive", ...
- JVM监测分析JConsole
一.基本操作 启动界面 1.JConsole是什么 从Java 5开始引入了JConsole.JConsole是一个内置Java性能分析器,可以从命令行或在GUI shell中运行.您可以轻松地使 ...
- DWZ(一):框架初了解
DWZ富client框架(jQuery RIAframework),是中国人自己开发的基于jQuery实现的Ajax RIA开源框架. DWZ富client框架设计目标是简单有用.扩展方便.高速开发. ...
- 倍福TwinCAT(贝福Beckhoff)应用教程13.2 TwinCAT控制松下伺服 CS说明
虚拟仿真上,要注意仿真只是为了可视化,可以看到数据的变动是否和实际一致,所以Robot2D才是主要因素,虚拟仿真采集机器人的关节位置或者TCP位置来显示而已,为了测试一些别的算法,我们还可以在虚拟仿真 ...
- 有关于apktool的使用的一些心得
<span style="font-family: Arial, Helvetica, sans-serif;">1.配置Java的环境</span> 1) ...
- 【Python3 爬虫】10_Beautiful Soup库的使用
之前学习了正则表达式,但是发现如果用正则表达式写网络爬虫,那是相当的复杂啊!于是就有了Beautiful Soup 简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓 ...
- 了解机器学习框架CoreML
代码地址如下:http://www.demodashi.com/demo/11972.html CoreML是iOS 11新推出的机器学习框架,是人工智能的核心内容,他可以在训练好的机器学习模型应用到 ...