Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1090475
  • 博文数量: 321
  • 博客积分: 7872
  • 博客等级: 少将
  • 技术积分: 2120
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-16 09:06
文章分类

全部博文(321)

文章存档

2017年(1)

2016年(1)

2015年(12)

2014年(17)

2013年(78)

2012年(15)

2011年(17)

2010年(67)

2009年(102)

2008年(11)

分类: LINUX

2009-08-13 12:14:20

Nginx的内存分配一共分成两层,第一层是对malloc()等函数进行了第一层的封装,例如malloc()对应的被封装成ngx_alloc()函数。

我们列一张对应表:


我们看一下ngx_alloc的源代码:

void *
ngx_alloc(size_t size, ngx_log_t *log)
{
    void *p;

    p = malloc(size);
    if (p == NULL) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "malloc() %uz bytes failed", size);
    }

    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);

    return p;
}

封装的好处在于对失败的内存分配进行了错误处理。

在这个基础上,nginx内存进行了第二层封装,也就是内存池,我们先列一下有哪些函数:




在Nginx的不同代码里都有对创建内存池的过程,我们总结一下:

init_cycle.pool = ngx_create_pool(1024, log);
pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); // NGX_CYCLE_POOL_SIZE = 16384
ha.temp_pool = ngx_create_pool(16384, cf->log);
pool = ngx_create_pool(16384, cf->log);
r->pool = ngx_create_pool(cscf->request_pool_size, c->log);
pool = ngx_create_pool(2048, s->connection->log);

16384实际上是16k内存,多数是16K为一个pool,也就是下面这张图:





因此在Nginx中创建了若干个pool的单向链表结构。

其中next指向同等大小的下一个pool,每个pool都用一个end指向自己的池底。

我们拿出其中一个pool分析进行分析,会得到下面这样一种结构:



以下是ngx_pool_s的结构定义:

struct ngx_pool_s {
    u_char               *last;
    u_char               *end;
    ngx_pool_t           *current;
    ngx_chain_t          *chain;
    ngx_pool_t           *next;
    ngx_pool_large_t     *large;
    ngx_pool_cleanup_t   *cleanup;
    ngx_log_t            *log;
};

每分配一个pool,它的第一个元素总是ngx_pool_t结构,我们在图中用header进行表示。

我把pool当中的小块,分成了item、large和cleanup三种,当待分配空间已经超过了池子自身大小,nginx也没有别的好办法,只好按照你需要分配的大小,实际去调用malloc()函数去分配,例如池子的大小是1K,待分配的大小是1M。实际上池子里只存储了ngx_pool_large_t结构,这个结构中的alloc指针,指向被分配的内存,并把这个指针返回给系统使用。

当然我们不希望large这种事太多,否则没有启到池子的作用。

当我们使用ngx_pool_cleanup_add()添加回收机制的时候,会在池子中分配一个ngx_pool_cleanup_t结构,并让header的cleanup指到这个结构上,这个结构的指针被返回给系统。

系统会对handler赋值一个反射函数,当我们调用ngx_destroy_pool()函数的时候,进行回收相关的处理。

当一个池子无法分配足够的空间,会继续调用ngx_create_pool()创建新的池子。

内存池的释放

ngx_destroy_pool()用来释放内存池,一共分三步:

第一步、在释放前先对业务逻辑进行释放前的处理

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

第二步、释放large占用的内存

    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->next; /* void */; p = n, n = n->next) {
        ngx_free(p);

        if (n == NULL) {
            break;
        }
    }
阅读(2096) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~