nginx内存池如何实现(nginxcpu内存)

nginx内存池如何实现(nginxcpu内存)

浏览次数:
信息来源: 用户投稿
更新日期: 2026-07-11
文章简介

最新稳定版本nginx1.20.2。为了能高效、快速的分配内存,以及减少内存碎片等,nginx实现了自己的内存池基础组件。主要实现文件 ngx_pool_cleanup_t*cleanup;

2025阿里云双十一服务器活动

最新稳定版本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);

nginx内存池如何实现,nginxcpu内存

}

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内存池如何实现的详细内容,更多请关注主机测评网其它相关文章!

    标签:
    ElasticSearch(elasticsearch的节点类型)
    « 上一篇
    返回列表
    下一篇 »

    如本文对您有帮助,就请抽根烟吧!