由于本人才疏学浅,本文难免存在遗漏之处,欢迎大家留言指正,本人将感激不尽。
Nginx使用内存池对内存进行管理,提高内存管理效率。对于小块内存,直接从已分配好的内存池中获取(返回指针);对于大块内存,直接调用malloc申请内存,然后将大块内存挂在内存池上进行管理(链表形式)。
对于内存的申请,Nginx还做了一些优化,比如在某一块内存池上申请小内存失败次数超过四次时,直接跳过该内存池,后续将不在该内存池上申请小块内存;
Nginx内存管理的核心结构体如下:
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; //下一次申请小块内存的pool
ngx_chain_t *chain;
ngx_pool_large_t *large; //保存大块内存的链表
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
};
对于小块内存,全部位于ngx_pool_s的数据区段,ngx_pool_s按链表形式组织;对于大块内存,全部以链表的形式存储,large指向分配的第一个大块内存。如下图所示:
下面主要通过代码介绍一下内存分配的过程
创建内存池
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;
}
分配内存如下:
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max) { //分配小块内存,直接在内存池上分配,移动指针
return ngx_palloc_small(pool, size, 1);
}
#endif
return ngx_palloc_large(pool, size); //分配大块内存,调用malloc创建size大小的内存,然后插入large链表
}
分配小块内存如下:
static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
u_char *m;
ngx_pool_t *p;
p = pool->current; //获取当前可申请小块内存的pool
/*在内存池链表中查找可分配小块内存的内存池*/
do {
m = p->d.last;
if (align) {
m = ngx_align_ptr(m, 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); //内存池链表中未找到可分配小块内存的内存池,则创建新的内存池
}
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new;
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;
for (p = pool->current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) { //如果超过四次分配小内存失败,则将current指针后移
pool->current = p->d.next;
}
}
p->d.next = new; //将新创建的内存池加入内存池链表尾部
return m;
}
分配大块内存如下:只有第一个内存池拥有大块内存链表
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;
//插入large链表中,寻找可插入的位置
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
if (n++ > 3) { //若连续三次未找到可插入的位置,则直接插入链表头部
break;
}
}
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL) {
ngx_free(p);
return NULL;
}
//插入链表头部
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}