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进行的分析,该 ...
随机推荐
- 蒟蒻kc的垃圾数列
题目背景 在某教练的强迫之下,我一个蒟蒻居然出题了!!!出题了!!!(数据太水别找我qwq) 好的,JL说好的一题100快拿来 题目描述 首先,给你一个空的长度为n的序列(废话) 然后,你有一系列神奇 ...
- Unity中FSM有限状态机
什么是FSM FSM 即有限状态机,它是一个状态管理系统,表示一个对象的几种状态在指定条件下转移行为,即随着条件的不断改变内部状态不断地切换. FSM用处或者使用背景 通常使用FSM去实现一些简单的A ...
- 如何 clean IntelliJ IDEA 中的工程
如何 clean IntelliJ IDEA 中的工程 1.点击“build”,选择“Build Artifacts” 2.点击“clean”,就可以了:然后重新,debug run 就完成了. ...
- 9款很棒的网页绘制图表JavaScript框架脚本
推荐9款很棒的可在网页中绘制图表的JavaScript脚本,这些有趣的JS脚本可以帮助你快速方便的绘制图表(线.面.饼.条…),其中包括jQuery.MooTools.Prototype和一些其它的J ...
- SSH的两种登录方式以及配置
前言 SSH简介 Secure Shell(SSH) 是由 IETF(The Internet Engineering Task Force) 制定的建立在应用层基础上的安全网络协议.它是专为远程登录 ...
- laravel 优化小记
laravel 优化 7 Performance Optimization Tips for the Laravel Developer 运行 php artisan optimize php art ...
- 【JZOJ6293】迷宫
description analysis 有没有想起[\(NOIP2018\)]保卫王国? 设\(tr[t][x][y]\)表示线段树上的\(t\)节点代表的区间,从最左边列的\(x\)行到最右边列\ ...
- 14 win7 sp1下安装vs2015
0 引言 在win7下安装vs2015的时候遇到了很多问题,看了很多帖子,尝试了很多次,终于成功了.网上也有大量关于win7 sp1下安装vs2015的帖子,我在安装的时候也参考了很多相关经验,这次写 ...
- fiddler过滤其他网站请求
1.首先选择filters过滤器,之后选择使用,点击第二个下拉框选择只显示下方主机地址 2.填写需要过滤的IP或网址,点击Actions保存设置 3.最后就可以只抓取固定的数据包了.
- Super OJ 序列计数
题意: 给出序列 a1,a2,--an(0≤ai≤109),求三元组(ai,aj,ak)(1≤i<j<k≤n)满足 ai<aj>ak 的数量. 分析: 开两个\(BIT\),分 ...