浅谈nginx内存池(三)

来源:互联网 发布:python logger 编辑:程序博客网 时间:2024/05/18 02:48

1.之前分别说过了内存池分配小块内存以及大块内存的情况。

但是如果当前的内存池已经满的时候,此时应该重新分配一块内存池,然后链接到当前的内存池的数据区指针---->ngx_palloc_block

需要注意三点:
(1)重新分配这个内存池的大小是和他的父内存池一样大的。
(2)子内存池和父内存池是不一样的。

(3)新的内存池只保存了ngx_pool_data_t这个结构,也就是说数据区直接跟在ngx_pool_data_t下面。

如图:

之前这幅图出现过,不过侧重点不一样,在这里我们主要是看重新分配的内存池的结构以及父子内存池的关联。

具体代码如下:
static void * ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new, *current;

///计算当前的内存池的大小。(减去ngx_pool_t的大小)
psize = (size_t) (pool->d.end - (u_char *) pool);

///再分配一个同样大小的内存池
m = ngx_alloc(psize, pool->log);
if (m == NULL) {
return NULL;
}

new = (ngx_pool_t *) m;

///接下来和我们create一个内存池做的操作一样。就是更新一些指针
new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = 0;

///这里要注意了,可以看到last指针是指向ngx_pool_data_t的大小再加上要分配的size大小
m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size;//在数据区分配size大小的内存

///设置current。
current = pool->current;

///这里遍历所有的子内存池,这里主要是通过failed来标记重新分配子内存池的次数,然后找出最后一个大于4的,标记它
for (p = current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) {
current = p->d.next;
}
}
///链接到最后一个内存池后面
p->d.next = new;

///如果current为空,则current就为new。
pool->current = current ? current : new;
return m;
}


说明:这里设置current为比较新分配的内存,主要原因是我们在ngx_palloc中分配内存是从current开始的。当failed大于4时,说明我们至少请求了4次内存分配,都不能满足我们的请求,此时我们就假设老的内存已经没有空间了,因此我们就从比较新的内存池开始分配。


2.看完了分配,接着不能少的就是释放啦!

不过我们需要知道,在nginx中,只有大块内存提供了free接口,可以供我们释放,而小块内存是没有这个接口的。小块内存只有当整个内存池被destory掉时,才会被释放。
其中有一些比较简单的函数:
(1)ngx_pfree(ngx_pool_t * pool,void *p):此函数就是从pool的large链表中找到p,然后free掉p。
(2)ngx_pool_cleanup_t * ngx_pool_cleanup_add(ngx_pool_t * p,size_t  size):此函数就是添加一个ngx_pool_cleanup_t到当前的pool上,然后返回,我们此时就可以通过返回的结构来给相应的handler赋值。
而ngx_pool_cleanup_t这个主要是当内存池destory的时候我们可能需要做一些清理工作,此时我们就可以add这些清理handler到pool中,然后当内存池要释放的时候就会自动调用。
下面是ngx_pool_cleanup_t的结构:
struct ngx_pool_cleanup_t
{
   ngx_pool_cleanup_pt  handler;
   void                 *data; //表示handler指向的函数可能需要用的参数
   ngx_pool_cleanup_t   *next;
};
而其中的ngx_cleanup_pt的结构如下:
typedef void (*ngx_pool_cleanup_pt)  (void *data);
很显然,这是一个函数指针,这一下问题显然就解决了,在内存管理中遇到这样的特殊的数据结构,只要编写它相应的释放函数,用函数指针加载到pool单元上,在注销pool内存池时调用即可。


备注:ngx_pool_cleanup_add函数他给ngx_pool_cleanup_t分配的内存空间也是pool单元中的内存,释放pool的时候一并释放出来。


(3)下面主要是看看void ngx_destory_pool(ngx_pool_t * pool)函数
具体代码如下:
void ngx_destroy_pool(ngx_pool_t *pool)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
ngx_pool_cleanup_t *c;

///先做清理工作。(处理ngx_pool_cleanup_t的特殊需求)
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);
}
}

///free大块内存
for (l = pool->large; l; l = l->next) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
if (l->alloc) {
ngx_free(l->alloc);
}
}

///遍历小块内存池。
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
///直接free掉。
ngx_free(p);
if (n == NULL) {
break;
}
}
}


注意:内存池的建设过程,是先建pool单元,如果有大内存需要再建large。而内存池的注销正好相反,首先是处理ngx_pool_cleanup_t的特殊需求,如果有的话然后释放分配的large,最后释放pool。

所以,通过上面可以看到在nginx内存池中的小块数据时从来不释放的,这样也简化了内存池的操作。

原创粉丝点击