nginx源码分析——内存池
内存池的目的就是管理内存,使回收内存可以自动化一些。
ngx_palloc.h
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/ #ifndef _NGX_PALLOC_H_INCLUDED_
#define _NGX_PALLOC_H_INCLUDED_ #include <ngx_config.h>
#include <ngx_core.h> // 内存池界定小内存与大内存的边界值的最大值。
/*
* NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
* On Windows NT it decreases a number of locked pages in a kernel.
*/
#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)
// 未知
#define NGX_DEFAULT_POOL_SIZE (16 * 1024) // 内存池的内存对齐值,即分配的内存大小是该值的倍数。
#define NGX_POOL_ALIGNMENT 16 // 未知
#define NGX_MIN_POOL_SIZE \
ngx_align((sizeof(ngx_pool_t) + * sizeof(ngx_pool_large_t)), \
NGX_POOL_ALIGNMENT) // 回收方法的模型
typedef void (*ngx_pool_cleanup_pt)(void *data); // 回收节点
typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;
struct ngx_pool_cleanup_s {
// handler,回收方法
ngx_pool_cleanup_pt handler;
// data指针,指向用于回收方法的传入参数
void *data;
// next指针,指向下一个回收节点
ngx_pool_cleanup_t *next;
}; // 大内存节点
typedef struct ngx_pool_large_s ngx_pool_large_t;
struct ngx_pool_large_s {
// next指针,指向下一个大内存节点
ngx_pool_large_t *next;
// alloc指针,指向大内存
void *alloc;
}; // 小内存块节点
typedef struct {
// last指针,指向内存块里已用空间的结束位,亦是可用空间的开始位。
u_char *last;
// end指针,指向内存块的结束位。
u_char *end;
// next指针,指向下一个内存块。虽然类型是内存池,但实际应用时视作内存块类型。
ngx_pool_t *next;
// failed,统计内存块因剩余空间不足以达到所需分配的内存大小而失败,且需要新建内存块才能分配所需内存的次数。
// 这个次数达到6次(从0累计到5)时,则下一次分配内存时,不管此内存块的剩余空间是否足够,都不会在此内存块上分配内存。
ngx_uint_t failed;
} ngx_pool_data_t; // 内存池
struct ngx_pool_s {
// d,内存池的第一个小内存块,意味着内存池自身是存储于第一个小内存块里。
ngx_pool_data_t d;
// max边界值,用于界定分配的内存是小内存还是大内存。
// max边界值,先受限于最大界值,再受限于初始内存大小。
size_t max;
// current指针,指向第一个可分配内存的小内存块。
ngx_pool_t *current;
ngx_chain_t *chain;
// large指针,指向大内存节点链头.
ngx_pool_large_t *large;
// cleanup指针,指向回收节点链头
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
}; // 文件类型的回收节点时,其data所需该类型
typedef struct {
// 文件句柄
ngx_fd_t fd;
// 文件名称
u_char *name;
ngx_log_t *log;
} ngx_pool_cleanup_file_t; // 创建内存池
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
// 销毁内存池
void ngx_destroy_pool(ngx_pool_t *pool);
// 重置内存池
void ngx_reset_pool(ngx_pool_t *pool); // 从内存池里分配内存(对齐内存)
void *ngx_palloc(ngx_pool_t *pool, size_t size);
// 分配内存(不对齐内存)
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
// 从内存池里分配内存(对齐内存),并填充为0。
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
// 从内存池里分配大内存(内存对齐,不使用空闲内存节点)
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
// 从内存池里释放指定的大内存(不释放节点)
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p); // 从内存池里创建一个回收节点,当size大于0时,将创建给定大小的内存,但这个内存并不受内存池管理,即需要创建者自行释放。
ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
// 从内存池里清理给定的文件类型的回收节点
void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);
// 关闭文件
void ngx_pool_cleanup_file(void *data);
// 删除和关闭文件
void ngx_pool_delete_file(void *data); #endif /* _NGX_PALLOC_H_INCLUDED_ */
ngx_palloc.c
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/ #include <ngx_config.h>
#include <ngx_core.h> static ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size,
ngx_uint_t align);
static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);
static void *ngx_palloc_large(ngx_pool_t *pool, size_t size); // 创建内存池
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);
if (p == NULL) {
return NULL;
} // last指针,指向内存块里已用空间的结束位,亦是可用空间的开始位。
p->d.last = (u_char *) p + sizeof(ngx_pool_t); // 内存池和内存块各自占一部份内存。
// end指针,指向内存块的结束位。
p->d.end = (u_char *) p + size;
p->d.next = NULL;
p->d.failed = ; // max边界值,用于界定分配的内存是小内存还是大内存。
// max边界值,先受限于最大界值,再受限于初始内存大小。
size = size - sizeof(ngx_pool_t);
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL; // current指针,表示当前内存块,当前内存块是处于可分配内存的状态
p->current = p;
p->chain = NULL;
p->large = NULL;
p->cleanup = NULL;
p->log = log; return p;
} // 销毁内存池
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) {
// handler,回收方法,为NULL时表示已回收。
if (c->handler) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, ,
"run cleanup: %p", c);
// data,用于调用回收方法时传入的参数。
c->handler(c->data);
}
} // 调试模式时,输出日志
#if (NGX_DEBUG) /*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/ for (l = pool->large; l; l = l->next) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, , "free: %p", l->alloc);
} for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, ,
"free: %p, unused: %uz", p, p->d.end - p->d.last); if (n == NULL) {
break;
}
} #endif // 释放大内存
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
} // 释放小内存
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_free(p); if (n == NULL) {
break;
}
}
} // 重置内存池
void
ngx_reset_pool(ngx_pool_t *pool)
{
ngx_pool_t *p;
ngx_pool_large_t *l; // 释放大内存
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
} // 重置小内存
for (p = pool; p; p = p->d.next) {
p->d.last = (u_char *) p + sizeof(ngx_pool_t);
p->d.failed = ;
} pool->current = pool;
pool->chain = NULL;
pool->large = NULL;
} // 从内存池里分配内存(对齐内存)
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
// max边界值,用于界定分配的内存是小内存还是大内存
if (size <= pool->max) {
// 分配小内存(对齐方式)
return ngx_palloc_small(pool, size, );
}
#endif
// 分配大内存
return ngx_palloc_large(pool, size);
} // 分配内存(不对齐内存)
void *
ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
// max边界值,用于界定分配的内存是小内存还是大内存
if (size <= pool->max) {
// 分配小内存
return ngx_palloc_small(pool, size, );
}
#endif
// 分配大内存
return ngx_palloc_large(pool, size);
} // 从内存池里分配小内存(私有)
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; // 从第一个可分配内存的内存块开始遍历
p = pool->current;
do {
// 获得可用空间的开始位
m = p->d.last; if (align) {
// 进行内存对齐
m = ngx_align_ptr(m, NGX_ALIGNMENT);
} // 判断剩余空间是否足够所需内存大小
if ((size_t) (p->d.end - m) >= size) {
// 如果足够,则分配此内存块
// last指针,指向内存块里已用空间的结束位,亦是可用空间的开始位。
p->d.last = m + size;
// 返回分配的内存
return m;
} // 如果不足够,则遍历下一个内存块。
p = p->d.next;
} while (p); // 如果没有内存块可分配所需内存大小,则从新建内存块里分配内存
return ngx_palloc_block(pool, size);
} // 从内存池里新建内存块进行分配内存(私有)
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new; // 创建同等大小的内存块。
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; // end指针,指向新内存块的结束位。
new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = ; // last指针,指向新内存块里已用空间的结束位,亦是可用空间的开始位。
m += sizeof(ngx_pool_data_t); // 新内存块自身占用一部份内存
m = ngx_align_ptr(m, NGX_ALIGNMENT); // 进行内存对齐
new->d.last = m + size; // 由于使用了新建内存块进行分配内存,所以意味着内存池的所有可分配内存块的剩余空间都不足以达到所需分配的内存大小。
for (p = pool->current; p->d.next; p = p->d.next) {
// failed,统计内存块因剩余空间不足以达到所需分配的内存大小而失败,且需要新建内存块才能分配所需内存的次数。
// 这个次数达到6次(从0累计到5)时,则下一次分配内存时,不管此内存块的剩余空间是否足够,都不会在此内存块上分配内存。
if (p->d.failed++ > ) {
// current指针,指向可分配内存的内存块。
pool->current = p->d.next;
}
} // 把新内存块加入链尾
p->d.next = new; // 返回分配的内存
return m;
} // 从内存池里分配大内存(私有,使用前5个空闲内存节点)
static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
void *p;
ngx_uint_t n;
ngx_pool_large_t *large; // 创建所需大小的内存
p = ngx_alloc(size, pool->log);
if (p == NULL) {
return NULL;
} n = ;
for (large = pool->large; large; large = large->next) {
// 判断内存节点是否为空
if (large->alloc == NULL) {
// 如果为空,则绑定内存到该内存节点。
// alloc指针,指向内存。
large->alloc = p;
// 返回内存
return p;
} // 累计未能找到空的内存块节点的次数,如果达到5次(从0到4),则结束寻找。
// 意味着空的内存节点最多存在5个。
if (n++ > ) {
break;
}
} // 创建内存节点
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), );
if (large == NULL) {
ngx_free(p);
return NULL;
} // alloc指针,指向内存。
large->alloc = p;
// next指针,指向下一个内存节点。
large->next = pool->large;
// 把新的内存节点添加到链头。
pool->large = large; return p;
} // 从内存池里分配大内存(内存对齐,不使用空闲内存节点)
void *
ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
{
void *p;
ngx_pool_large_t *large; // 创建内存
p = ngx_memalign(alignment, size, pool->log);
if (p == NULL) {
return NULL;
} // 创建内存节点(内存对齐)
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), );
if (large == NULL) {
ngx_free(p);
return NULL;
} // alloc指针,指向内存。
large->alloc = p;
// next指针,指向下一个内存节点。
large->next = pool->large;
// 把新的内存节点添加到链头。
pool->large = large; return p;
} // 从内存池里释放指定的大内存(不释放节点)
ngx_int_t
ngx_pfree(ngx_pool_t *pool, void *p)
{
ngx_pool_large_t *l; for (l = pool->large; l; l = l->next) {
if (p == l->alloc) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, ,
"free: %p", l->alloc);
ngx_free(l->alloc);
l->alloc = NULL; return NGX_OK;
}
} return NGX_DECLINED;
} // 从内存池里分配内存(对齐内存),并填充为0。
void *
ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
void *p; // 创建内存
p = ngx_palloc(pool, size);
if (p) {
// 填充0
ngx_memzero(p, size);
} return p;
} // 从内存池里创建一个回收节点,当size大于0时,将创建给定大小的内存,但这个内存并不受内存池管理,即需要创建者自行释放。
ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
ngx_pool_cleanup_t *c; // 创建回收节点
c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
if (c == NULL) {
return NULL;
} // data,用于调用回收方法时传入的参数。
if (size) {
// 创建内存,但这个内存并不受内存池管理,即需要创建者自行释放。
c->data = ngx_palloc(p, size);
// BUG?如果创建内存失败,此时的回收节点还未加入到链头,意味着回收节点不能释放?
if (c->data == NULL) {
return NULL;
} } else {
c->data = NULL;
}
// handler,回收方法。
c->handler = NULL; // 加入链头
c->next = p->cleanup;
p->cleanup = c; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, , "add cleanup: %p", c); return c;
} // 从内存池里清理给定的文件类型的回收节点
void
ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
{
ngx_pool_cleanup_t *c;
ngx_pool_cleanup_file_t *cf; for (c = p->cleanup; c; c = c->next) {
if (c->handler == ngx_pool_cleanup_file) {
// data,用于调用回收方法时传入的参数。
cf = c->data;
if (cf->fd == fd) {
// handler,回收方法,为NULL时表示已回收。
c->handler(cf); // 这里实际执行的方法是ngx_pool_cleanup_file
c->handler = NULL;
return;
}
}
}
} // 关闭文件
void
ngx_pool_cleanup_file(void *data)
{
ngx_pool_cleanup_file_t *c = data; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, , "file cleanup: fd:%d",
c->fd); // 执行文件关闭
// fd,文件句柄
if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", c->name);
}
} // 删除和关闭文件
void
ngx_pool_delete_file(void *data)
{
ngx_pool_cleanup_file_t *c = data; ngx_err_t err; ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, , "file cleanup: fd:%d %s",
c->fd, c->name); // 执行文件删除
// name,文件名称
if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {
err = ngx_errno; if (err != NGX_ENOENT) {
ngx_log_error(NGX_LOG_CRIT, c->log, err,
ngx_delete_file_n " \"%s\" failed", c->name);
}
} // 执行文件关闭
// fd,文件句柄
if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", c->name);
}
} #if 0 static void *
ngx_get_cached_block(size_t size)
{
void *p;
ngx_cached_block_slot_t *slot; if (ngx_cycle->cache == NULL) {
return NULL;
} slot = &ngx_cycle->cache[(size + ngx_pagesize - ) / ngx_pagesize]; slot->tries++; if (slot->number) {
p = slot->block;
slot->block = slot->block->next;
slot->number--;
return p;
} return NULL;
} #endif
nginx源码分析——内存池的更多相关文章
- 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 ...
- nginx源码分析线程池详解
nginx源码分析线程池详解 一.前言 nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,n ...
- nginx源码学习----内存池
最近在进行监控平台的设计,之前一直觉得C/C++中最棘手的部分是内存的管理上,远不止new/delete.malloc/free这么简单.随着代码量的递增,程序结构复杂度的提高.各种内存方面的问题悄然 ...
- nginx源码分析——线程池
源码: nginx 1.13.0-release 一.前言 nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影 ...
- linux内存源码分析 - 内存池
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 内存池是用于预先申请一些内存用于备用,当系统内存不足无法从伙伴系统和slab中获取内存时,会从内存池中获取预留的 ...
- Nginx源码分析:3张图看懂启动及进程工作原理
编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由陈科在高可用架构群分享.转载请注明来自高可用架构公众号「ArchNotes」. 导读:很多工程师及架构师都希望了解及掌握高性能服务器 ...
- Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求
Elasticsearch源码分析—线程池(十一) 转自:https://www.felayman.com/articles/2017/11/10/1510291570687.html 线程池 每个节 ...
- JUC源码分析-线程池篇(二)FutureTask
JUC源码分析-线程池篇(二)FutureTask JDK5 之后提供了 Callable 和 Future 接口,通过它们就可以在任务执行完毕之后得到任务的执行结果.本文从源代码角度分析下具体的实现 ...
- nginx源码分析-源码结构
本文主要简单介绍nginx源码目录结构.程序编译流程.如何构建学习nginx的环境等.本文以及后续nginx源码分析文章是基于nginx当前(2009-02-27)的稳定版本0.6.35进行的分析,该 ...
随机推荐
- 11-MySQL-Ubuntu-数据表中数据的删除(四)
数据的删除(delete) (1)物理删除(不可逆,公司不会采取这种方法,如现在一般不会出现注销,数据具有无限价值) 删除整张表的数据!!! delete from 表名; 删除部分给定条件的数据: ...
- Neo4j中实现自定义中文全文索引
数据库检索效率时,一般首要优化途径是从索引入手,然后根据需求再考虑更复杂的负载均衡.读写分离和分布式水平/垂直分库/表等手段:索引通过信息冗余来提高检索效率,其以空间换时间并会降低数据写入的效率:因此 ...
- 简单的GridView分业,后台不需要写
1前台代码: <asp:GridView ID="GridView1" runat="server" AllowPaging="True&quo ...
- Python_pymysql
pymysql安装:pip3 install pymysql 第一个实例:连接数据库进行用户验证 条件:数据库中已存在一个用户表,包含用户名.密码 import pymysql user = in ...
- 从 i++ 和 ++i 说起局部变量表和操作数栈
本文转载自:从 i++ 和 ++i 说起局部变量表和操作数栈 最近公司有人看了尚硅谷柴林燕老师的第一季面试题,就想来考考我.我觉得柴老师讲的很好,部分内容可以延伸一下,所以写这篇文章分享给大家! 这篇 ...
- iOS开发之SceneKit框架--SCNScene.h
1.SCNScene SCNScene是一个场景图——具有附加几何形状.光照.摄像机和其他属性的节点的层次结构,共同形成可显示的3D场景. 2.相关API简介 初始化方法 //懒加载 + (insta ...
- Delphi代码创建形式规范 1.0
Delphi代码创建形式规范 1.0 本规范的目的:给自己的代码一个统一而标准的外观,增强 可读性,可理解性,可维护性 本规范的原则:名称反映含义,形式反映结构 1.单元风格 ...
- day22_3-json模块
# 参考资料:# python模块(转自Yuan先生) - 狂奔__蜗牛 - 博客园# https://www.cnblogs.com/guojintao/articles/9070485.html# ...
- qfc 问题汇总(TO BE CONTINUED)
硬件问题 增加一个复位按钮 程序问题 /* uart allocation: PB6-7: UART1 -> TELEM PD5-6 : UART2 -> SBUS PD8-9: UART ...
- Django的日常-模型层(2)
目录 Django的日常-模型层(2) 几种常用的查询方式 聚合查询 分组查询 F和Q查询 查询优化相关 orm中常见字段 choices参数 orm的事务操作 Django的日常-模型层(2) 几种 ...