Nginx的内存管理一直以来我并没有去仔细研究,只是被动的利用,直到项目中有需要,才真正的开始吮吸这方面的内容,好了,闲话少叙,开始看看这部分的代码实现:
struct ngx_pool_s {
ngx_pool_data_t d;//该成员变量中存放的是真正的数据内容,接下来我们会去看看
size_t max;//内存池的最大值,超过max的在large中分配
ngx_pool_t *current;//指向当前使用的内存池
ngx_chain_t *chain;//暂且不说只是ngx_buf_t的链表
ngx_pool_large_t *large;//对于大内存的分配,在这里面进行分配
ngx_pool_cleanup_t *cleanup;//清理内存的函数指针
ngx_log_t *log;//日志
};
现在看一下ngx_pool_data_t的数据结构
typedef struct {
u_char *last;//指向当前可用内存的起始地址
u_char *end;//指向当前内存块的结束位置
ngx_pool_t *next;//指向下一个内存池
ngx_uint_t failed;//当前内存池分配内存失败的次数
} ngx_pool_data_t;
现在说下ngx_create_pool的函数具体操作
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);//该段代码可以看出,创建ngx_pool_t之后,他的可用内存起始地址还要减去ngx_pool_t结构体的大小
p->d.end = (u_char *) p + size; //d.end指向内配内存的结束位置
p->d.next = NULL;//下一个内存池指向NULL
p->d.failed = 0;//分配失败次数为0
size = size - sizeof(ngx_pool_t);//可用内存大小减去ngx_pool_t结构体的大小,进行校正
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;//max值的设定是根据内存对齐值进行设定的,
p->current = p;//设置当前使用内存池是我们新建的这个
p->chain = NULL;
p->large = NULL;
p->cleanup = NULL;
p->log = log;
return p;
}
现在分析一个内存开辟函数
ngx_palloc
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
u_char *m;
ngx_pool_t *p;
//首先检测,max与size的关系,如果size小于max则认为是小内存
if (size <= pool->max) {
//首先在当前内存池中进行分配
p = pool->current;
do {
//得到当前内存对其的内存地址
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
//如果当前可用内存大小大于申请的内存的话,则直接返回该地址,然后将内存池中的可用内存地址移动到申请大小之后的地址
if ((size_t) (p->d.end - m) >= size) {
p->d.last = m + size;
return m;
}
p = p->d.next;
} while (p);
//如果执行到这里说明所有内存池中并没有可用大小大于申请大小的内存池,然后执行ngx_palloc_block
return ngx_palloc_block(pool, size);
}
return ngx_palloc_large(pool, size);
}
接下来看一下对于没有申请到合适大小的内存的话,则执行ngx_palloc_block
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);//申请同样大小的内存池,并把其实地址指向m
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进行内存对其,即可
m = ngx_align_ptr(m, NGX_ALIGNMENT);
//在新的内存池中的可用内存的起始地址移动到size之后
new->d.last = m + size;
for (p = pool->current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) {
pool->current = p->d.next;
}
}
//最后一个内存池加上新建的内存池
p->d.next = new;
//返回可用地址,因为申请内存的大小肯定小于max,所以新内存池的大小肯定能够分配size大小的内存
return m;
}
现在看一下ngx_palloc_large函数
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;
//将申请的大内存挂载内存池当中,成功则直接返回
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
//尝试挂载3此失败,则停止
if (n++ > 3) {
break;
}
}
//挂载失败,执行到此
large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
if (large == NULL) {
ngx_free(p);
return NULL;
}
//将该大内存池放在当前大内存池的前面
large->alloc = p;
large->next = pool->large;
pool->large = large;
// 返回内存地址
return p;
}
接下来要说的是内存的清理
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p)
需要注意的是该函数只释放large链表中注册的内存,普通内存在ngx_destroy_pool中统一释放。