剖析Nginx内存池(重要类型定义和创建内存池函数)
重要宏
内存池的设计,一般都会有大小内存的分界线
SGI STL内存池的应用场景主要是容器内存的分配释放,而Nginx是一个http服务器,包含了很多重要的模块,它的内存池是提供给所有模块使用的,它的大小内存的分界线是一个页面的大小,也就是4K,比SGI STL的128字节大的多。
- NGX_DEFAULT_POOL_SIZE:内存池的默认大小,为16K
- NGX_POOL_ALIGNMENT:内存对齐单位,为16
- NGX_MIN_POOL_SIZE:内存池最小的大小,其定义用到了另一个宏,该宏定义为:
#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
这个宏的作用和SGI STL中的_S_round_up函数作用一样,用来提升到最邻近对齐字节倍数。其中的ngx_pool_t类型和ngx_pool_large_t类型后面再介绍,在这只需理解这个宏的作用即可。
重要函数
//创建内存池
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
//销毁内存池
void ngx_destroy_pool(ngx_pool_t *pool);
//重置内存池
void ngx_reset_pool(ngx_pool_t *pool);
//分配内存,考虑内存对齐
void *ngx_palloc(ngx_pool_t *pool, size_t size);
//分配内存,不考虑内存对齐
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
//分配内存并初始化为0
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
//释放内存
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);
重要struct
//小块内存链表普通节点
typedef struct {
u_char *last;
u_char *end;
ngx_pool_t *next;
ngx_uint_t failed;
} ngx_pool_data_t;
//大块内存头部记录
typedef struct ngx_pool_large_s ngx_pool_large_t;
struct ngx_pool_large_s {
ngx_pool_large_t *next;
void *alloc;
};
//内存池头部信息
typedef struct ngx_pool_s ngx_pool_t;
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 *
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;
}
首先从堆申请内存,Nginx用两个宏来确定是否字节对齐,如果申请失败则输出错误日志,然后返回空指针。
申请成功则用一个ngx_pool_t
类型的指针指向申请内存的起始地址,ngx_pool_t类型是用来记录一个内存池的状态的头信息,一个ngx_pool_t对应一个内存池。
然后把ngx_pool_t的d.last和d.end成员分别指向申请内存块的起始地址和末尾地址;d.next 表示下块小块内存池,初始化为 NULL,d.failed 表示小块内存申请失败的次数,初始化为 0;max记录小块内存池的最大大小,不超过4095也就是一个页面的大小。current表示分配小块内存的当前块,初始化为内存池头。其初始化结构如下图: