ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Nginx源码理解 - 内存池

2021-06-30 22:31:27  阅读:207  来源: 互联网

标签:void Nginx 源码 内存 ngx cleanup pool size


现在对于nginx的内存分配函数讲解有很多了,就不一一分析了,讲下心得和实现的方式。个人觉得实现的模式和思维这才是很是我们学习的地方。知道原理、学会思考才能熟记于心。

这是第一次讲对nginx的理解,有什么不对的地方请大家多多指教,毕竟nginx涉及了太多知识点了,尤其是对系统的深刻理解。

目录

一、为什么要使用内存池

二、思考如何实现内存池,而nginx怎么做的

三、nginx设计的内存池完美无瑕吗?

四、常用函数小记


一、为什么要使用内存池

一般我们使用malloc/alloc/free等函数来分配和释放内存。但是直接使用这些函数会有一些弊端:

1、虽然系统自带的ptmalloc内存分配管理器,也有自己的内存优化管理方案(申请内存块以及将内存交还给系统都有自己的优化方案,具体可以研究一下ptmalloc的源码),但是直接使用malloc/alloc/free,仍然会导致内存分配的性能比较低

2、频繁使用这些函数分配和释放内存,会导致内存碎片,不容易让系统直接回收内存。典型的例子就是大并发频繁分配和回收内存,会导致进程的内存产生碎片,并且不会立马被系统回收,利用性就不是很好。

3、内存管理不当,容易产生内存泄露

如何解决以上3点,其实还是得靠扎实的基础知识,一一对应上面的解决方法是:

1、https://blog.csdn.net/bandaoyu/article/details/107988843 这里测试内存分配性能,结论也是证明了频繁开启内存效率会低。所以我们应该采用开启一大块内存,在这块内存上做分配。

2、由于linux内存机制,它的回收机制不是释放了就马上回收,而是系统有一个专门的内核线程用来定期回收内存或者在有新的大块内存分配请求时,但剩余内存不足。这个时候系统就需要回收一部分内存。所以我们在一大块内存上,做的是相对的“开启”和“回收”,能充分利用内存;

 3、在一大块内存中即使管理不当也不会产生严重的内存泄露,回收的时候只需要回收大块内存就能将所有的内存回收。

二、思考如何实现内存池,而nginx怎么做的

OK,如果我们要自己实现内存管理功能——内存池,那么我们可能在思考的时候就会抛出几个问题:

1、如何管理我的开辟的这一大块内存?

2、内存池初始化分配的这一大块内存有什么讲究,要开多大呢?

3、如果之后开辟的内存超过内存池初始化分配的内存怎么办 ?

4、等等......

这里nginx怎么做的呢?一一对应上面的方法是:

1、内存的管理采用了链表的形式串起每块内存。用last指针指向未使用的内存,且为了防止链表过于长导致查询效率低的时候,大于4个节点就会把查询链表头current指针指向最后一个。(这里思考一下为什么选择大于4个节点)

2、针对每个对象,要初始化分配的内存大小不一样,但是考虑系统原因加入了pagesize的这一个判断点。如main函数中的对init_cycle的内存池就分配了1024的初始大小。实际大小是min(size-sizeof(ngx_pool_t),ngx_pagesize-1)。

3、分2种情况:

        a、如果想从内存池开辟很大一块内存,那么为了防止开辟太大让链表加长的,内存池直接采用了一个large链表给你专门分配大内存。(思考多大才算大?应该也有考虑系统原因和初始内存大小)

        b、如果内存池之前那块内存不够分了,它有个current指针指向新分配的一个内存块(也是ngx_pool_t),且通过链表的形式连接起来。

当然,nginx还做了1个cleanup链表来专门存放释放内存的handler方法。

三、nginx设计的内存池完美无瑕吗?

1、如果我们内存池使用了一部分内存后,如果我前面的内存释放了,那如何能重复利用到前面已经释放的内存?

2、ngx_reset_pool重置(并不释放初始分配的内存)之后,并不是完全还原到初始状态,而是链表头及节点后的内存分配区域被浪费了,如下图:

3、也是说明了这里有个小坑,Nginx 假性内存泄露

四、常用函数小记

void *ngx_alloc(size_t size, ngx_log_t *log);//malloc的封装
void *ngx_calloc(size_t size, ngx_log_t *log);//malloc的封装,内存经过初始化
 
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);//重置内存池,会释放大块内存
static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);//申请新内存空间

void *ngx_palloc(ngx_pool_t *pool, size_t size);//申请内存,内存经过对齐
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);//申请内存,内存没有经过对齐

#ifndef NGX_ALIGNMENT
#define NGX_ALIGNMENT   sizeof(unsigned long)    /* platform word */
#endif

#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1))
#define ngx_align_ptr(p, a)                                                   \
    (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
两者差别就是ngx_palloc中调用了ngx_align_ptr做了地址对齐。可以先看ngx_align比较好理解。
也就是大于d且大于a的最小2次幂的数字。如果a不是2的幂那计算就不成立了。
(思考那什么时候要用到ngx_pnalloc呢?)

void *ngx_pcalloc(ngx_pool_t *pool, size_t size);//申请内存,内存经过对齐,且申请的内存经过初始化
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);//申请大块内存,内存经过对齐
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);//释放所有大块内存
 
ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);//添加cleanup大小为size(data)
void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);//清楚所有的cleanup
void ngx_pool_cleanup_file(void *data);//关闭文件,data指向ngx_pool_cleanup_file_t
void ngx_pool_delete_file(void *data);//删除文件,data指向ngx_pool_cleanup_file_t

参考:

https://blog.csdn.net/xiaoliangsky/article/details/39523875

https://blog.csdn.net/mrpre/article/details/85273831

http://blog.chinaunix.net/uid-25424552-id-4466925.html

https://blog.csdn.net/initphp/article/details/50588790

标签:void,Nginx,源码,内存,ngx,cleanup,pool,size
来源: https://blog.csdn.net/qq_23282479/article/details/118369531

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有