内存池结构体
struct ngx_pool_s { ngx_pool_data_t d; /* 内存池链表头指针 */ size_t max; /* 一个阈值,决定是用malloc分配内存还是从内存池分配 */ ngx_pool_t *current; /* 这个指针指向第一个可以分配小块内存的节点 */ ngx_chain_t *chain; ngx_pool_large_t *large; /* 大于max用malloc分配内存,并生成ngx_pool_large_t结构体,大块内存链表头指针 */ ngx_pool_cleanup_t *cleanup; /* 需要释放的资源,fd,文件...,这也是个链表 */ ngx_log_t *log; /* 日志 */ };
nginx的内存池在分配空间时有两种方式,一种是从内存池分配,另外一种用malloc分配,
分配方式的选择由预分配字节大小来决定,如果预分配字节大于max,就会使用malloc分配。
整个内存池就是一个单链表,其通过ngx_pool_data_t这个结构体链起来的,对于这个单链
表,每个节点都是个内存池,内存池大小一样,但是只有第一个内存池具有完整的ngx_pool_s
结构体结构,其他内存池的只有一个ngx_pool_data_t结构体:typedef struct { u_char *last; /* 内存池中当前未分配区域的首指针 */ u_char *end; /* 内存池尾指针 */ ngx_pool_t *next; /* 下一个ngx_pool_t */ ngx_uint_t failed; /* 分配的失败次数,即内存池剩余字节不足分配,这个与ngx_pool_s的current结构体有关 */ } ngx_pool_data_t;
内存池通过此结构体的next指针链接起来形成单链表
创建内存池的函数:/* 创建内存池 至少分配的内存要大于sizeof(ngx_pool_t) 分配的内存会挂载ngx_pool_data_t中 这个就是内存池链表的头结点,分配的内存的头部sizeof(ngx_pool_t)字节将会存储 */ 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_POOL_ALIGNMENT对齐 */ 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); 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; }
再来看从内存池分配size(size <= max)字节的函数
/* 按照align分配的内存是否和总线长度对齐 */ 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; /* 从current指针来遍历链表,而不是遍历整个链表 */ 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) { p->d.last = m + size; return m; } p = p->d.next; } while (p); /* 所有ngx_pool_data_t的剩余空间都小与size了 */ return ngx_palloc_block(pool, size); } /* 处理内存池链表上面所有节点的剩余空间全部不足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); /* 内存池加上sizeof(ngx_pool_t)的总字节大小 因为第一个ngx_pool_t的d.end指针指向内存池尾, 第一个ngx_pool_t的地址是内存池首 */ /* 重新分配一个内存池 */ 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; new->d.failed = 0; m += sizeof(ngx_pool_data_t); /* 不需要是sizeof(ngx_pool_t),因为其他成员只需要在第一块内存池的中保存一份 */ m = ngx_align_ptr(m, NGX_ALIGNMENT); new->d.last = m + size; /* 更新整个内存池链表的节点的分配失败次数 */ for (p = pool->current; p->d.next; p = p->d.next) { /* 一旦内存池分配小块内存失败四次,currrent就会移到下一个内存池节点 */ if (p->d.failed++ > 4) { pool->current = p->d.next; } } /* 将新分配的内存池节点挂到链表尾 */ p->d.next = new; return m; }
大块内存分配
大块内存结构体 struct ngx_pool_large_s { ngx_pool_large_t *next; /* 链表下一节点 */ void *alloc; /* 指向分配的大块内存 */ };
在分配大块内存的时候,sizeof(ngx_pool_large_s)将会从内存池分配,alloc将会指向malloc分配的内存,
由于大块内存一般较大,nginx提供提前释放大块内存的函数,对于释放大块内存时,仅仅释放alloc指针指向的
内存,并将alloc置为NULL,在分配大块内存时,分配的内存块将会先遍历大块内存链表,找到是否有提前释放的
节点,如果有,就把分配的内存挂到此节点上,即alloc指针上。
接下来看分配大块内存的函数:
/* 分配大块内存,返回其alloc指针 */
static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
void *p;
ngx_uint_t n;
ngx_pool_large_t *large;
/* 大块内存直接使用malloc来分配 */
p = ngx_alloc(size, pool->log);
if (p == NULL) {
return NULL;
}
n = 0;
/* 大块内存将挂载到大块内存链表上
首先找到有没有ngx_pool_large_t的alloc的内存已经释放 */
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
/* 最多只会遍历三个节点,因为前面的是最早分配的,也有可能最早释放,如果前面三个节点都
没有释放,在遍历就有可能白费功夫 */
if (n++ > 3) {
break;
}
}
/* ngx_pool_large_t需要的内存由小块内存分配 */
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL) {
ngx_free(p);
return NULL;
}
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
释放内存池及重置内存池
/* 销毁整个内存池,ngx_pool_t是一个单链表 */ 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) { if (c->handler) { ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "run cleanup: %p", c); c->handler(c->data); } } /* 释放大块内存,仅仅释放alloc指针指向的内存,并不会释放ngx_pool_large_t结构体 ngx_pool_large_t的释放由下面循环来释放 */ for (l = pool->large; l; l = l->next) { if (l->alloc) { ngx_free(l->alloc); } } /* 释放ngx_pool_t结构体,这个首指针将指向分配的小块内存,因此这里会释放整个ngx_pool_t,也就是整个小的内存池 ngx_pool_large_t的结构体占用的空间是从小内存池中分配的 */ for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_free(p); if (n == NULL) { break; } } } /* 重置pool 大块内存释放,小块内存不释放,但是内存中的数据将忽略 */ 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); } } /* 小块内存将会重置,重置的过程中ngx_pool_large_t也会"释放" */ for (p = pool; p; p = p->d.next) { p->d.last = (u_char *) p + sizeof(ngx_pool_t); p->d.failed = 0; } pool->current = pool; pool->chain = NULL; pool->large = NULL; } /* 提前释放ngx_pool_large_t的alloc内存 */ 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, 0, "free: %p", l->alloc); ngx_free(l->alloc); l->alloc = NULL; return NGX_OK; } } return NGX_DECLINED; }