nginx内存池管理分析
原载于:文章来源:www.forasp.cn网站制作学习
最近修改nginx看了一下nginx的内存管理代码,将nginx的内存池管理拿出来分享一下
/////////////////////////////////////////
//1.创建一个pool内存池,并返回起始地址
//////////////////////////////////////////
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);
//ngx_memalign()函数执行内存分配,该函数的实现在src/os/unix/ngx_alloc.c文件中(假定NGX_HAVE_POSIX_MEMALIGN被定义):
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);//将size减去管理结构的大小,然后判断
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
//最大不超过4095B,别忘了上面NGX_MAX_ALLOC_FROM_POOL的定义
p->current = p;//将管理结构的current指向自己起始地址,下面的定义chain,large,cleanupd等
p->chain = NULL;
p->large = NULL;
p->cleanup = NULL;
p->log = log;
return p;
}
////////////////////////////////////////////////////////////////////////
//2.申请对齐的内存空间,并返回申请成功空间的起始地址(上面函数9行调用了)
///////////////////////////////////////////////////////////////////////
void *
ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
{
void *p;
int err;
err = posix_memalign(&p, alignment, size);
//该函数分配以alignment为对齐的size字节的内存大小,其中p指向分配的内存块。
if (err) {
ngx_log_error(NGX_LOG_EMERG, log, err,
"posix_memalign(%uz, %uz) failed", alignment, size);
p = NULL;
}
ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,
"posix_memalign: %p:%uz @%uz", p, size, alignment);
return p;
}
////////////////////////////////////////////////////////////////////////
//3.销毁内存池 pool ,也就是上面1创建的内存池
//////////////////////////////////////////////////////////////////////////
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) {//循环调用内存池中所有内容的clearup回调函数
if (c->handler) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"run cleanup: %p", c);
c->handler(c->data);
}
}
//前面讲到,cleanup指向析构函数,用于执行相关的内存池销毁之前的清理工作,如文件的关闭等,
//清理函数是一个handler的函数指针挂载。因此,在这部分,对内存池中的析构函数遍历调用。
for (l = pool->large; l; l = l->next) { //清理对应的pool下面挂在的large大的内存空间,擦已用原始的free
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
if (l->alloc) {
ngx_free(l->alloc);
}
}
//这一部分用于清理大块内存,ngx_free实际上就是标准的free函数,
//即大内存块就是通过malloc和free操作进行管理的。
#if (NGX_DEBUG)
/**
* we could allocate the pool->log from this pool
* so we can not use this log while the free()ing the pool
*/
//如果再debug模式下打印所有的pool使用信息,当循环到最后一个pool之后,则跳出循环
for (p = pool, n = pool->d.next; /** void */; p = n, n = n->d.next) {
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"free: %p, unused: %uz", p, p->d.end - p->d.last);
if (n == NULL) {
break;
}
}
//只有debug模式才会执行这个片段的代码,主要是log记录,用以跟踪函数销毁时日志记录。
#endif
//这里是彻底销毁所有的pool,当前pool的->d的链表,直到结束。
for (p = pool, n = pool->d.next; /** void */; p = n, n = n->d.next) {
ngx_free(p);
if (n == NULL) {
break;
}
}
}
//该片段彻底销毁内存池本身。
//////////////////////////////////////////////////////////////////
//4.重置内存池pool
/////////////////////////////////////////////////////////////////
void
ngx_reset_pool(ngx_pool_t *pool)
{
ngx_pool_t *p;
ngx_pool_large_t *l;
//查询当前pool下面挂的所有的large内存块直接free
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
}
//将pool的large的指针置空
pool->large = NULL;
//将所有的pool->d的的pool的last指向 当前所属pool的起始位置(pool开头+pool结构大小的偏移)
for (p = pool; p; p = p->d.next) {
p->d.last = (u_char *) p + sizeof(ngx_pool_t);
}
}
///////////////////////////////////////////////////////////////////
//5.内存分配函数,从pool中申请size大小的空间
///////////////////////////////////////////////////////////////////
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
u_char *m;
ngx_pool_t *p;
//判断待分配内存与max值
//1、小于max值,则从current结点开始遍历pool链表
if (size <= pool->max) {//判断当前的pool大小是不是足够申请的,如果足够的话。
//pool取当前指向的pool
p = pool->current;
do {
//执行对齐操作,
//即以last开始,计算以NGX_ALIGNMENT对齐的偏移位置指针,,获取对齐偏移后的位置,从last对齐,也许就是last也许要+n个b(具体看内存管理)
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
//然后计算end值减去这个偏移指针位置的大小是否满足索要分配的size大小,
//如果满足,则移动last指针位置,并返回所分配到的内存地址的起始地址,这样就从pool分配了size大小的空间
if ((size_t) (p->d.end - m) >= size) {
p->d.last = m + size;
//在该结点指向的内存块中分配size大小的内存
return m;
}
//如果不满足,则查找当前pool链上的下一个pool
p = p->d.next;
} while (p);//如果又下一个pool则继续,如果没有则跳出,表示找不到何时的size
//如果没有合适的pool则,重新申请pool
return ngx_palloc_block(pool, size); //2.4.1节分析
}
//2、如果大于max值,则执行大块内存分配的函数ngx_palloc_large,在large链表里分配内存
return ngx_palloc_large(pool, size); //2.4.2节分析
}
/////////////////////////////////////////////////////////////////
//6. 当需要的内存大于pool目前可用内存大小时,则创建一个新的pool
/////////////////////////////////////////////////////////////////
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new, *current;
psize = (size_t) (pool->d.end - (u_char *) pool);
//计算当前的pool的大小,即当前pool的整块的大小,也就是整个pool的大小(包括pool的结构和数据空间)
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);//创建对齐的一个pool大小空间
if (m == NULL) {
return NULL;
}
//执行按NGX_POOL_ALIGNMENT对齐方式的内存分配,假设能够分配成功,则继续执行后续代码片段。
//将m强转换为pool结构
new = (ngx_pool_t *) m;
//设置pool的结束为止
new->d.end = m + psize;
//将新pool的next置空
new->d.next = NULL;
new->d.failed = 0;
//执行该block相关的初始化。
//偏移pool的写入内容地址
m += sizeof(ngx_pool_data_t);
//让m指向该块内存ngx_pool_data_t结构体之后数据区起始位置
m = ngx_align_ptr(m, NGX_ALIGNMENT);//将m进行内存对齐
new->d.last = m + size;//将使用到的位置偏移,也就是用了size大小的空间,则将last偏移
//获取当盘的current pool
current = pool->current;
//循环当前pool,循环如果错误超过4次的pool则将current指向下一个
for (p = current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) {
current = p->d.next;
//失败4次以上移动current指针,如果所有的pool错误都错误3次了,则最后current指向了null
}
}
//将新的pool挂到 当盘pool->d链的最后一个
p->d.next = new;
//将分配的block链入内存池
//如果所有的pool错误次数都大于3,则将current指向新的pool
pool->current = current ? current : new;
return m;
}
////////////////////////////////////////////
//7. 创建大的内存空间
//注释:要的内存大于pool最大可分配内存大小时,此时首先判断size已经大于pool->max的大小了,所以直接调用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;
p = ngx_alloc(size, pool->log);//直接分配size空间
if (p == NULL) {
return NULL;
}
n = 0;
//如果在前三个large中有空闲结构(没有指向具体的地址),则直接将申请的空间给当前的large结构
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
//如果链表大于3 则将重新申请一个管理结构,并挂到链头部(下面继续)
if (n++ > 3) {
break;
}
}
//如果该pool之前并未分配large内存,则就没有ngx_pool_large_t来管理大块内存
//执行ngx_pool_large_t结构体的分配,用于来管理large内存块。
large = ngx_palloc(pool, sizeof(ngx_pool_large_t));//这里是从当前的pool里面分出一个large的结构空间
if (large == NULL) {
ngx_free(p);
return NULL;
}
//将large结构赋值,并将large挂到当前盘pool的large的最开始
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
//////////////////////////////////////////////////////////////////
//8.直接分配large的大空间 在7 的236行调用
/////////////////////////////////////////////////////////////////
void *
ngx_alloc(size_t size, ngx_log_t *log)
{
void *p;
p = malloc(size);
//从这里可以看到,ngx_alloc实际上就是调用malloc函数分配内存的。
if (p == NULL) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"malloc() %uz bytes failed", size);
}
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);
return p;
}
//////////////////////////////////////////////////////////////////
//9.直接分配large的大空间并置零 ,调用上面的函数8
/////////////////////////////////////////////////////////////////
void *
ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
void *p;
p = ngx_palloc(pool, size);
if (p) {
ngx_memzero(p, size);
}
return p;
}
<%77w%77%2Ef%6F%72p%73%70%2Ec%6E>