浅谈nginx内存池(二)

来源:互联网 发布:五线胆码的算法 编辑:程序博客网 时间:2024/06/05 00:14
1.接下来就进入到具体的函数中。

首先是创建一个内存池----->ngx_create_pool

 

///内存池的数据区的最大容量。

#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) //此处的ngx_pagesize的大小在x86下通常是4k

ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log)

{

ngx_pool_t *p;

///可以看到直接分配size大小,也就是说我们只能使用size-sizeof(ngx_pool_t)大小

p = ngx_alloc(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 = 0;

///这里才是我们真正能使用的大小。

size = size - sizeof(ngx_pool_t);

///然后设置max。内存池的最大值也就是size和最大容量之间的最小值。

p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

///current表示当前的内存池。

p->current = p;

///其他的域置NULL

p->chain = NULL;

p->large = NULL;

p->cleanup = NULL;

p->log = log;

return p;

}

 

2.接下来是如何从内存池中分配一块内存。

如图:

(1)分配一个较小的内存:图(a)或者图(b

             

       (a)                                               (b)

(2)分配一个较大的内存:


有以下3个函数:

(1)ngx_palloc:这个函数分配的内存会对齐。

(2)ngx_calloc:这个函数用来分配一块清零的内存。

(3)ngx_pnalloc:这个函数分配的内存是不对齐的。

这三个函数差不多,就只需要分析一个就好。

void * ngx_palloc(ngx_pool_t *pool, size_t size)

{

u_char *m;

ngx_pool_t *p;

///首先判断当前申请的大小是否超过max,如果超过则说明是大块,此时进入large

if (size <= pool->max) {

///得到当前的内存池指针。

p = pool->current;

///开始遍历内存池,

do {

///首先对齐last指针。

m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);

/*

#define ngx_align_ptr(p, a) \

   (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

 

这个宏使得到的内存地址为NGX_ALIGNMENT的倍数。数据对齐,可以避免cpu取值时,要进行两次IO

*/

///然后得到当前内存池中的可用大小。如果大于请求大小,则直接返回当前的last,也就是数据的指针。

if ((size_t) (p->d.end - m) >= size) {

///更新last,然后返回前面保存的last

p->d.last = m + size;

return m;

}

///否则继续遍历

p = p->d.next;

} while (p);

///到达这里说明内存池已经满掉,因此我们需要重新分配一个内存池然后链接到当前的datanext上。

return ngx_palloc_block(pool, size);

}

///申请大块。

return ngx_palloc_large(pool, size);

}

说明:这个函数的第一个参数是size,虽然我们传递进去的是size,但是我们真正可以使用的数据区大小是size-sizeof(ngx_pool_t)


3.分配一块较大的内存,需要malloc一块ngx_pool_large_t,然后链接到内存池上。--->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);

if (p == NULL) {

return NULL;

}

n = 0;

///开始遍历large链表,如果有alloc(也就是内存区指针)为空,则直接指针赋值然后返回。一般第一次请求大块内存都会直接返回

for (large = pool->large; large; large = large->next) {

if (large->alloc == NULL) {

large->alloc = p;

return p;

}

if (n++ > 3) {

break;

}

}

///malloc一块ngx_pool_large_t

large = ngx_palloc(pool, sizeof(ngx_pool_large_t));

if (large == NULL) {

ngx_free(p);

return NULL;

}

///然后链接数据区指针plarge。这里可以看到直接插入到large链表的头的。

large->alloc = p;

large->next = pool->large;

pool->large = large;

return p;

}