同样,紧接着,咱们就来分析上述代码中的ngx_palloc_block()函数:
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的大小,即需要分配的block的大小
m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
if (m == NULL) {
return NULL;
}
//执行按NGX_POOL_ALIGNMENT对齐方式的内存分配,假设能够分配成功,则继续执行后续代码片段。
//这里计算需要分配的block的大小
new = (ngx_pool_t *) m;
new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = 0;
//执行该block相关的初始化。
m += sizeof(ngx_pool_data_t);
//让m指向该块内存ngx_pool_data_t结构体之后数据区起始位置
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size;
//在数据区分配size大小的内存并设置last指针
current = pool->current;
for (p = current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) {
current = p->d.next;
//失败4次以上移动current指针
}
}
p->d.next = new;
//将分配的block链入内存池
pool->current = current ? current : new;
//如果是第一次为内存池分配block,这current将指向新分配的block。
return m;
}
注意:该函数分配一块内存后,last指针指向的是ngx_pool_data_t结构体(大小16B)之后数据区的起始位置,而创建内存池时时,last指针指向的是ngx_pool_t结构体(大小40B)之后数据区的起始位置。 结合2.8节的内存池的物理结构,更容易理解。
b、待分配内存大于max值的情况
如2.4.1节所述,如果分配的内存大小大于max值,代码将跳到ngx_palloc_large(pool, size)位置,ok,下面进入ngx_palloc_large(pool, size)函数的分析:
//这是一个static的函数,说明外部函数不会随便调用,而是提供给内部分配调用的,//即nginx在进行内存分配需求时,不会自行去判断是否是大块内存还是小块内存,
//而是交由内存分配函数去判断,对于用户需求来说是完全透明的。
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); //下文紧接着将分析此ngx_alloc函数
if (p == NULL) {
return NULL;
}
n = 0;
//以下几行,将分配的内存链入pool的large链中,
//这里指原始pool在之前已经分配过large内存的情况。
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
if (n++ > 3) {
break;
}
}
//如果该pool之前并未分配large内存,则就没有ngx_pool_large_t来管理大块内存
//执行ngx_pool_large_t结构体的分配,用于来管理large内存块。
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;
}