最新稳定版本nginx1.20.2。为了能高效、快速的分配内存,以及减少内存碎片等,nginx实现了自己的内存池基础组件。主要实现文件
ngx_pool_cleanup_t*cleanup;
};
内存池中第一个成员是一个结构体:使用ngx_pool_data_t结构体来表示当前内存池信息。last:下次开始分配的地址end:内存池的结束地址next:内存池链表,将多个内存池连接起来
current指向从当前内存池开始查找可用内存
chainbuffer使用的,这里不涉及
large当需要的内存大于内存池最大大小时,需要通过malloc直接分配,然后形成链表进行组织
cleanup清理工作的回调链表
当需要分配的内存比内存池的最大大小都大时,内存池无法满足分配,所以直接从系统中分配,然后构成一个链表进行维护。
typedefstructngx_pool_large_sngx_pool_large_t;
有一个回调任务的链表,当内存池销毁时,将依次遍历此链表,逐一回调handler进行清理工作。
typedefvoid(*ngx_pool_cleanup_pt)(void*data);
typedefstructngx_pool_cleanup_sngx_pool_cleanup_t;
structngx_pool_cleanup_s{
ngx_pool_cleanup_pthandler;
};
三、内存结构图
可以看出,很多节点都是从内存池中分配的,所以可以把精力都放在实际的数据上而不必在意其他细节上。
#defineNGX_MAX_ALLOC_FROM_POOL(ngx_pagesize-1)
#defineNGX_DEFAULT_POOL_SIZE(16*1024)
ngx_pool_t*
ngx_create_pool(size_tsize,ngx_log_t*log)
p=ngx_memalign(NGX_POOL_ALIGNMENT,size,log);
p->d.last=(u_char*)p+sizeof(ngx_pool_t);
p->d.end=(u_char*)p+size;
size=size-sizeof(ngx_pool_t);
p->max=(size<NGX_MAX_ALLOC_FROM_POOL)?size:NGX_MAX_ALLOC_FROM_POOL;
}
从代码中可以看到,内存池最大不超过pagesize的大小
分配函数分了内存对齐和内存不对齐,但这只控制了内存池中分配空间,不控制大内存分配。
ngx_palloc(ngx_pool_t*pool,size_tsize)
if(size<=pool->max){
returnngx_palloc_small(pool,size,1);
returnngx_palloc_large(pool,size);
}
当需要分配的空间小于max时,将使用小内存分配方式(即从内存池中分配空间),而ngx_pnalloc和ngx_palloc相比只是调用ngx_palloc_small时的最后一个参数为0。
从pool->current指向的内存池开始遍历,寻找满足分配大小的空间,找到则返回首地址
ngx_palloc_small(ngx_pool_t*pool,size_tsize,ngx_uint_talign)
m=ngx_align_ptr(m,NGX_ALIGNMENT);
if((size_t)(p->d.end-m)>=size){
returnngx_palloc_block(pool,size);
}
当现有内存池中都无法满足分配条件时,创建新的内存池
ngx_palloc_block(ngx_pool_t*pool,size_tsize)
psize=(size_t)(pool->d.end-(u_char*)pool);
m=ngx_memalign(NGX_POOL_ALIGNMENT,psize,pool->log);
m+=sizeof(ngx_pool_data_t);
m=ngx_align_ptr(m,NGX_ALIGNMENT);
for(p=pool->current;p->d.next;p=p->d.next){
if(p->d.failed++>4){
pool->current=p->d.next;
}
其中,创建好新的内存池后,又做了一次遍历,将failed计数加一,当大于4时,将跳过此内存池,下次就不从它开始查找。即认为超过4次你都不能满足分配,以后都不能满足分配,不再用你了,减少遍历个数,加快成功分配效率
ngx_palloc_large(ngx_pool_t*pool,size_tsize)
p=ngx_alloc(size,pool->log);
for(large=pool->large;large;large=large->next){
if(large->alloc==NULL){
large=ngx_palloc_small(pool,sizeof(ngx_pool_large_t),1);
large->next=pool->large;
}
可以看出,为了避免分配空间,遍历large链查找可重用的节点,但是如果链表过大又可能太慢,所以只查找前三个,如果三个都没有找到,则直接分配(而且节点也是从内存池中分配的,所以后续清理时,不需要管节点,只需要释放申请的大内存本身)
ngx_pmemalign(ngx_pool_t*pool,size_tsize,size_talignment)
p=ngx_memalign(alignment,size,pool->log);
large=ngx_palloc_small(pool,sizeof(ngx_pool_large_t),1);

}
4.3注册清理任务
ngx_pool_cleanup_add(ngx_pool_t*p,size_tsize)
c=ngx_palloc(p,sizeof(ngx_pool_cleanup_t));
c->data=ngx_palloc(p,size);
c->next=p->cleanup;
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC,p->log,0,"addcleanup:%p",c);
}
可以看出,这里只是分配了一个节点,并没有设置handler以及data数据,所以还得看具体的调用方进行设置,因为这里返回了分配的节点。
ngx_create_temp_file(ngx_file_t*file,ngx_path_t*path,ngx_pool_t*pool,
ngx_uint_tpersistent,ngx_uint_tclean,ngx_uint_taccess)
cln=ngx_pool_cleanup_add(pool,sizeof(ngx_pool_cleanup_file_t));
file->fd=ngx_open_tempfile(file->name.data,persistent,access);
if(file->fd!=NGX_INVALID_FILE){
cln->handler=clean?ngx_pool_delete_file:ngx_pool_cleanup_file;
clnf->name=file->name.data;
clnf->log=pool->log;
}
生成临时文件,将fd以及文件名注册到清理任务中,后续文件不使用了则不需要特殊处理,内存内存池释放时将统一清理。
ngx_reset_pool(ngx_pool_t*pool)
for(l=pool->large;l;l=l->next){
for(p=pool;p;p=p->d.next){
p->d.last=(u_char*)p+sizeof(ngx_pool_t);
}
这里有个现象:在内存池中空间不足时,将调用创建一个新的内存池,而last指向的是,因此当前新分配的内存池将比第一个内存池可用大小多了(max,current,chain,large,cleanup,log)这几个字段大小(可能没有那么多,因为要对齐,可能对齐后就完全一样了),而现在重置时,每个内存池可用大小又变成一样的。
ngx_destroy_pool(ngx_pool_t*pool)
for(c=pool->cleanup;c;c=c->next){
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC,pool->log,0,
c->handler(c->data);
for(l=pool->large;l;l=l->next){
for(p=pool,n=pool->d.next;;p=n,n=n->d.next){
通过遍历找到要释放的节点,将内存释放,并且将alloc设置成NULL,则有了节点重用的情况。
ngx_pfree(ngx_pool_t*pool,void*p)
for(l=pool->large;l;l=l->next){
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC,pool->log,0,
}
4.7分配并清空数据
ngx_pcalloc(ngx_pool_t*pool,size_tsize)
}
正常分配的空间中都是垃圾数据,所以当前函数在分配空间后,将分配的空间清零。
遍历清理任务,找到ngx_pool_cleanup_file的handler,如果是要关闭的fd,则回调
ngx_pool_run_cleanup_file(ngx_pool_t*p,ngx_fd_tfd)
ngx_pool_cleanup_file_t*cf;
for(c=p->cleanup;c;c=c->next){
if(c->handler==ngx_pool_cleanup_file){
ngx_pool_cleanup_file(void*data)
ngx_pool_cleanup_file_t*c=data;
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC,c->log,0,"filecleanup:fd:%d",
if(ngx_close_file(c->fd)==NGX_FILE_ERROR){
ngx_log_error(NGX_LOG_ALERT,c->log,ngx_errno,
ngx_close_file_n""%s"failed",c->name);
}
(3)删除文件并关闭fd
ngx_pool_delete_file(void*data)
ngx_pool_cleanup_file_t*c=data;
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC,c->log,0,"filecleanup:fd:%d%s",
if(ngx_delete_file(c->name)==NGX_FILE_ERROR){
ngx_log_error(NGX_LOG_CRIT,c->log,err,
ngx_delete_file_n""%s"failed",c->name);
if(ngx_close_file(c->fd)==NGX_FILE_ERROR){
ngx_log_error(NGX_LOG_ALERT,c->log,ngx_errno,
ngx_close_file_n""%s"failed",c->name);
}
以上就是nginx内存池如何实现的详细内容,更多请关注主机测评网其它相关文章!
本文来源:国外服务器--nginx内存池如何实现(nginxcpu内存)
本文地址:https://www.idcbaba.com/guowai/5745.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 1919100645@qq.com 举报,一经查实,本站将立刻删除。



