理解 Nginx 源码之二内存池管理

Nginx 内存池管理

概述

Nginx 使用内存池对内存进行管理,内存管理的实现类似于前面文章介绍的《STL源码剖析——空间配置器》,把内存分配归结为大内存分配小内存分配。若申请的内存大小比同页的内存池最大值 max 还大,则是大内存分配,否则为小内存分配。

  1. 大块内存的分配请求不会直接在内存池上分配内存来满足请求,而是直接向系统申请一块内存(就像直接使用 malloc 分配内存一样),然后将这块内存挂到内存池头部的 large 字段下。
  2. 小块内存分配,则是从已有的内存池数据区中分配出一部分内存。

Nginx 内存管理相关文件:

  1. src/os/unix/ngx_alloc.h/.c
  • 内存相关的操作,封装了最基本的内存分配函数。
  • 如 free / malloc / memalign / posix_memalign,分别被封装为 ngx_free,ngx_alloc / ngx_calloc, ngx_memalign
  • ngx_alloc:封装malloc分配内存
  • ngx_calloc:封装malloc分配内存,并初始化空间内容为0
  • ngx_memalign:返回基于一个指定 alignment 的大小为 size 的内存空间,且其地址为 alignment 的整数倍,alignment 为2的幂。
  1. src/core/ngx_palloc.h/.c
  • 封装创建/销毁内存池,从内存池分配空间等函数。
理解 Nginx 源码之二内存池管理

nginx内存分配

内存池基本结构

Nginx 内存池基本结构定义如下:

理解 Nginx 源码之二内存池管理

大块内存分配的数据结构如下:

理解 Nginx 源码之二内存池管理

其他数据结构如下:

理解 Nginx 源码之二内存池管理

内存池基本机构之间的关系如下图所示:

理解 Nginx 源码之二内存池管理

ngx_pool_t 的逻辑结构

上面数据结构之间逻辑结构图如下:该图是采用 UML 画的,第一行黑色粗体表示对应数据结构,第二行是结构内的成员,冒号左边是变量,冒号右边是变量的类型;

理解 Nginx 源码之二内存池管理

ngx_pool_t 的逻辑结构

内存池的操作

创建内存池

理解 Nginx 源码之二内存池管理

创建内存池

其中内存分配函数 ngx_memalign 定义如下:

理解 Nginx 源码之二内存池管理

内存分配函数ngx_memalign

销毁内存池

销毁内存池由 void ngx_destroy_pool(ngx_pool_t *pool) 函数完成。该函数将遍历内存池链表,释放所有内存,如果注册了clenup (也是一个链表结构),亦将遍历该 cleanup 链表结构依次调用 clenup 的 handler 清理。同时,还将遍历 large 链表,释放大块内存。

理解 Nginx 源码之二内存池管理

重置内存池

重置内存池由 void ngx_reset_pool(ngx_pool_t *pool) 函数完成。该函数将释放所有 large 内存,并且将 d->last 指针重新指向 ngx_pool_t 结构之后数据区的开始位置,使内存池恢复到刚创建时的位置。由于内存池刚被创建初始化时是不包含大块内存的,所以必须释放大块内存。

理解 Nginx 源码之二内存池管理

重置内存池

内存分配

小块内存分配

小块内存分配,即请求分配空间 size 小于内存池最大内存值 max。小内存分配的接口函数如下所示

理解 Nginx 源码之二内存池管理

ngx_palloc 和 ngx_pnalloc 都是从内存池里分配 size 大小内存。他们的不同之处在于,palloc 取得的内存是对齐的,pnalloc 则不考虑内存对齐问题。ngx_pcalloc 是直接调用 palloc 分配内存,然后进行一次 0 初始化操作。ngx_pmemalign 将在分配 size 大小的内存并按 alignment 对齐,然后挂到 large 字段下,当做大块内存处理。

ngx_palloc的过程一般为,首先判断待分配的内存是否大于 pool->max,如果大于则使用 ngx_palloc_large 在 large 链表里分配一段内存并返回, 如果小于测尝试从链表的 pool->current 开始遍历链表,尝试找出一个可以分配的内存,当链表里的任何一个节点都无法分配内存的时候,就调用 ngx_palloc_block 生成链表里一个新的节点, 并在新的节点里分配内存并返回, 同时, 还会将pool->current 指针指向新的位置(从链表里面pool->d.failed小于等于4的节点里找出) 。

理解 Nginx 源码之二内存池管理

分配内存

理解 Nginx 源码之二内存池管理

分配内存

理解 Nginx 源码之二内存池管理

小内存分配之后如下图所示:

理解 Nginx 源码之二内存池管理

上图是由3个小内存池组成的内存池模型,由于第一个内存池上剩余的内存不够分配了,于是就创建了第二个新的内存池,第三个内存池是由于前面两个内存池的剩余部分都不够分配,所以创建了第三个内存池来满足用户的需求。由图可见:所有的小内存池是由一个单向链表维护在一起的。这里还有两个字段需要关注,failed和current字段。failed表示的是当前这个内存池的剩余可用内存不能满足用户分配请求的次数,即是说:一个分配请求到来后,在这个内存池上分配不到想要的内存,那么就failed就会增加1;这个分配请求将会递交给下一个内存池去处理,如果下一个内存池也不能满足,那么它的failed也会加1,然后将请求继续往下传递,直到满足请求为止(如果没有现成的内存池来满足,会再创建一个新的内存池)。current字段会随着failed的增加而发生改变,如果current指向的内存池的failed达到了4的话,current就指向下一个内存池了。

大块内存分配

理解 Nginx 源码之二内存池管理

分配大块内存

理解 Nginx 源码之二内存池管理

释放大块内存

大块内存申请之后如下所示:

理解 Nginx 源码之二内存池管理

cleanup 资源

理解 Nginx 源码之二内存池管理

注册cleanup

理解 Nginx 源码之二内存池管理

清理内存池的文件

理解 Nginx 源码之二内存池管理

关闭data指定的文件句柄

理解 Nginx 源码之二内存池管理

删除data所指向的文件

《 Nginx源码剖析之内存池,与内存管理》

《nginx源码分析—内存池结构ngx_pool_t及内存管理》

《Nginx内存池实现源码分析 》

《Nginx源码分析-内存池》

《Ningx代码研究》


分享到:


相關文章: