参考:
http://blog.csdn.net/livelylittlefish/article/details/6586946
http://code.google.com/p/nginxsrp/wiki/NginxCodeReview
相关的源码文件为:
- nginx_palloc.h
- nginx_palloc.c
- nginx_alloc.h
- nginx_alloc.c
内存池数据块结构
typedef struct { u_char *last; // 当前内存池分配到此处,即下一次分配从此处开始 u_char *end; // 内存池结束位置 ngx_pool_t *next; // 内存池里面有很多块内存,这些内存块就是通过该指针连成链表的 ngx_uint_t failed; // 内存池分配失败次数 } ngx_pool_data_t; |
内存池结构如下
struct ngx_pool_s { ngx_pool_data_t d; // 指向内存池的第一个数据块 size_t max; // 内存池数据块的最大值(数目) ngx_pool_t *current; // 指向当前内存池 ngx_chain_t *chain; // 该指针挂接一个ngx_chain_t结构 ngx_pool_large_t *large; // 大块内存链表,即分配空间超过max的内存 ngx_pool_cleanup_t *cleanup; // 释放内存池的callback ngx_log_t *log; // 主要用于记录日志信息 }; |
large内存链表结构
struct ngx_pool_large_s { ngx_pool_large_t *next; // 指向下一个large内存 void *alloc; // 指向分配的large内存 }; |
cleanup内存链表结构
struct ngx_pool_cleanup_s { ngx_pool_cleanup_pt handler; // 指向用于cleanup本cleanup内存 void *data; // 指向分配的cleanup内存 ngx_pool_cleanup_t *next; // 指向下一个cleanup内存 }; |
创建内存池
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; } // 可以看到 last 指向 pool 之后的位置,即下一个pool块分配的位置 p->d.last = (u_char *) p + sizeof(ngx_pool_t); // end 指向pool的size的最后,即当前pool可容纳的最大尺寸的结束位置 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_MAX_ALLOC_FROM_POOL的定义如下:
/* * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86. * On Windows NT it decreases a number of locked pages in a kernel. */ #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) |
而ngx_pagesize的值依赖于OS的,取值如下设置:
ngx_int_t ngx_os_init(ngx_log_t *log) { ngx_uint_t n; #if (NGX_HAVE_OS_SPECIFIC_INIT) if (ngx_os_specific_init(log) != NGX_OK) { return NGX_ERROR; } #endif ngx_init_setproctitle(log); ngx_pagesize = getpagesize(); ngx_cacheline_size = NGX_CPU_CACHE_LINE; for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ } … … } |
其中getpagesize()是由OS提供的。
首先调用ngx_memalign(…)来分配alignedmemory。之后,初始化内存池结构的各个成员,下图为初始化好的内存池的结构图(调用ngx_create_pool(1024,0x80d1c4c)函数):
销毁内存池
void 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) … … #endif for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_free(p); if (n == NULL) { break; } } } |
遍历内存池链表,释放所有内存,包括pool,large,cleanup链表,如果指定了cleanup回调来释放,则调用cleanup的handler来释放cleanup链表中的内存。
先依次释放pool中cleanup,large类型的链表,最后释放pool本身的链表。
重置内存池
void ngx_reset_pool(ngx_pool_t *pool) { ngx_pool_t *p; ngx_pool_large_t *l; // 先遍历large链表,释放large内存 for (l = pool->large; l; l = l->next) { if (l->alloc) { ngx_free(l->alloc); } } pool->large = NULL; for (p = pool; p; p = p->d.next) { p->d.last = (u_char *) p + sizeof(ngx_pool_t); } } |
使用ngx_palloc()分配内存
void * ngx_palloc(ngx_pool_t *pool, size_t size) { u_char *m; ngx_pool_t *p; if (size <= pool->max) { // 如果需要分配的size大于max,则使用palloc_large来分配 p = pool->current; // 小于max,则从current开始遍历pool链表 do { // 每次从last处开始分配aligned内存 m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); if ((size_t) (p->d.end - m) >= size) { // 如果分配的内存够用,则就从此处分配,并调整last p->d.last = m + size; return m; } p = p->d.next; } while (p); // 表示链表里没有能够分配size大小的内存节点 // 则生成一个新的节点,并在其中分配内存 return ngx_palloc_block(pool, size); } // 大于max的,就在large中进行分配 return ngx_palloc_large(pool, size); } |
下图为在调用ngx_palloc(pool,200)分配200B的内存池物理结构图:
内存池的物理结构
注:文章中的图都摘自
(http://blog.csdn.net/livelylittlefish/article/details/6586946)
转载于:https://blog.51cto.com/quietmadman/1309952