参考:
http://blog.youkuaiyun.com/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)函数):
Nginx内存池实现的了解_nginx 内存池
销毁内存池
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的内存池物理结构图:
Nginx内存池实现的了解_nginx 内存池_02
内存池的物理结构
Nginx内存池实现的了解_nginx 内存池_03
https://blog.51cto.com/quietmadman/1309952