在上一篇中,以一张图(图1)介绍了nginx内存池管理函数的总体概况,并分析了底层支持函数和内存池(pool)管理所需要用到的一些数据结构。这里我们将来具体分析内存池管理功能函数。由上篇图1我们可以看出ngx_palloc为内存分配的核心,为此我们先看看这个函数。
void * ngx_palloc(ngx_pool_t *pool, size_t size) { u_char *m; ngx_pool_t *p; /*
注意2个if,3个return
*/ if (size <= pool->max) { p = pool->current; do { m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); if ((size_t) (p->d.end - m) >= size) { p->d.last = m + size; return m; } p = p->d.next; } while (p); return ngx_palloc_block(pool, size); } return ngx_palloc_large(pool, size); }
函数结构很明晰,2个if,1个do...while循环查找,3个return,就此我们可以得出Nginx内存分配时会出现三种情况。
第一,size <= pool->max 即所需要提供分配的内存小于Nginx内存池 pool允许容纳的最大值,那么这个时候开始顺着内存池pool的链表找,找着了能够分配需要大小size的内存空间,修改这个pool使用的内存尾指针last,并返回分配内存的首地址m。
第二,size <= pool->max 但是查找发现当前存在的内存池pool链表里的pool单元都没有足够的内存分配,那么调用 ngx_palloc_block(pool, size),我们这里就可以看出来ngx_palloc_block 函数是用来产生新的pool单元。
第三,如果size > pool->max,即所需要的提供分配的内存大于Nginx内存池 pool允许容纳的最大值,这说明即使生成新的pool单元也无法分配出这么大的需求,那么调用 ngx_palloc_large(pool, size)进行大内存单元分配。
因为,ngx_palloc是内存池分配的核心函数,为了更便于理解,罗嗦一下再附上张简单的示意图:
图2 pool_alloc示意图
了解ngx_palloc功能的流程后,我们可以发现ngx_palloc由ngx_palloc_block 和 ngx_palloc_large 两大支柱函数来完成些它无法完成的功能。查看ngx_palloc_block 源码,和我们先前分析出来的一样构造一个新的pool单元并记录下内存池分配失败的次数, ngx_palloc_large 用ngx_pool_large_t 结构体来对大内存需要分配内存,值得注意的是每个ngx_pool_large_t链表只能允许4个ngx_pool_large_t,如果超过4个大块内存分配请求,就会建造新的ngx_pool_large_t资源链表。详情见源码
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 = 0; for (large = pool->large; large; large = large->next) { if (large->alloc == NULL) { large->alloc = p; return p; } /*链表不能超过四个*/ if (n++ > 3) { break; } } /*新的大内存块链块*/ large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); if (large == NULL) { ngx_free(p); return NULL; } large->alloc = p; large->next = pool->large; pool->large = large; return p; }
此篇就分析到此处。