Chinaunix首页 | 论坛 | 博客
  • 博客访问: 229989
  • 博文数量: 59
  • 博客积分: 1215
  • 博客等级: 少尉
  • 技术积分: 575
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-09 02:18
文章分类

全部博文(59)

文章存档

2012年(53)

2011年(6)

分类: C/C++

2012-03-04 03:36:06

今天来探讨一下Nginx的内存分配策略。

那咱们先看看内存池长什么样子吧。

struct ngx_pool_s {
    ngx_pool_data_t       d;          
    size_t                max;
    ngx_pool_t           *current;
    ngx_chain_t          *chain;
    ngx_pool_large_t     *large;
    ngx_pool_cleanup_t   *cleanup;
    ngx_log_t            *log;
};

我们从实际函数中来研究这个内存池是怎样运作、那些成员有什么用,先来看看怎样创建内存池:

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);
    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;
}

好了,可以看到,创建内存池是初始化一些成员,设置大小。

接着,我们从内存池分配内存用的是ngx_palloc,ngx_calloc,ngx_pnalloc。这三个函数的区别就是第一个函数分配的内存会对齐。第二个函数用来分配一块清
0的内存,第三个函数分配的内存不会对齐。

我们这里看第一个。

void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
    u_char      *m;
    ngx_pool_t  *p;

    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);
}

这里可以知道内存池连起来的。

看到代码 if (size <= pool->max)  就是看一个内存池最大的大小够不够你放,够的话,进入里面。

p = pool->current; 这一句说明current成员指向的是当前内存池。

接下来是一个循环,就是要找当前内存池中还够不够你需要的大小,不够就找下一块内存池。

如果每个内存池都找不到,就ngx_palloc_block。

如果从一开始if (size <= pool->max) 这里就知道一个内存池根本满足不了欲望,好,那就ngx_palloc_large。

 

那么ngx_palloc_block和ngx_palloc_large究竟有什么作用呢?

我们接着看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);

    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);
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    new->d.last = m + size;

    current = pool->current;

    for (p = current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {
            current = p->d.next;
        }
    }

    p->d.next = new;

    pool->current = current ? current : new;

    return m;
}

显然,这个函数是新建一个全新的内存池,并加到现有的内存池链条中。

 

接着看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;

    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;
}

这里补充一下内存池中有一个large的指针,类型为

struct ngx_pool_large_s {
    ngx_pool_large_t     *next;
    void                 *alloc;
};

可以看到这是一个链表,存着真实的数据,而在内存池里面仅仅存放一个链表。

代码很容易理解。注意这里用了些技巧,把新建的大块内存确保在链表中前四的位置。

迟些时候继续分析销毁内存池。


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);
}
}

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);
}
}

#if (NGX_DEBUG)

/*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/

for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"free: %p, unused: %uz", p, p->d.end - p->d.last);

if (n == NULL) {
break;
}
}

#endif

for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_free(p);

if (n == NULL) {
break;
}
}
}

其实这个很容易看懂,如果我们原来设有内存池的清理函数,则调用。然后释放大块内存,最后释放小块,都是用ngx_free(也就是free)掉。

阅读(1502) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~