`
zzc1684
  • 浏览: 1188623 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

Nginx Proxy Cache的slab page内存缓存机制

阅读更多

Nginx的内存缓存是通过slab pool来实现的,但是目前Nginx代码没有对http响应进行内存缓存。比如作为反向代理服务器时向后端获取的文件也只是缓存在磁盘里,而内存只是用 来做索引。不过Nginx已经提供了内存缓存功能的函数,所以如果在其他地方有需要使用内存缓存的话,也可以通过修改代码来实现(当然,也可以用 memory disk来实现内存缓存)。在Nginx的内存缓存机制中,最重要的结构就是ngx_slab_pool_t,里面存放了包括内存缓存的空间使用情况、位 置映射以及缓存空间本身的几乎所有信息。先来看一下ngx_slab_pool_t吧。

C代码  收藏代码
  1. typedef struct {  
  2.     ngx_atomic_t        lock;   //mutex的锁  
  3.     size_t          min_size;   //内存缓存obj最小的大小,一般是1个byte  
  4.     size_t          min_shift;  //slab pool以shift来比较和计算所需分配的obj大小、  
  5.                                 //每个缓存页能够容纳obj个数以及所分配的页在缓存空间的位置  
  6.     ngx_slab_page_t *pages;     //slab page空间的开头  
  7.     ngx_slab_page_t free;       //如果要分配新的页,就从free.next开始  
  8.     u_char          *start;     //实际缓存obj的空间的开头  
  9.     u_char          *end;       //整个缓存空间的结尾  
  10.     ngx_shmtx_t     mutex;      //互斥锁  
  11.     u_char          *log_ctx;  
  12.     u_char          zero;  
  13.     void            *data;        
  14.     void            *addr;      //指向ngx_slab_pool_t的开头  
  15. } ngx_slab_pool_t;  
 
C代码  收藏代码
  1. struct ngx_slab_page_s {  
  2.     uintptr_t       slab;   //多种情况,多个用途  
  3.                             //当需要分配新的页的时候,slab表示剩余页的数量  
  4.                             //当分配某些大小的obj的时候(一个缓存页存放多个obj),slab表  
  5.                             //示被分配的缓存的占用情况(是否空闲),以bit位来表示  
  6.     ngx_slab_page_t *next;  //在分配较小obj的时候,next指向slab page在pool->pages的位置  
  7.     uintptr_t       prev;  
  8. };  
 

注意,在ngx_slab_pool_t里面有两种类型的slab page,虽然都是ngx_slab_page_t定义的结构,但是功能不尽相同。一种是slots,用来表示存放较小obj的内存块(如果页大小是 4096B,则是<2048B的obj,即小于1/2页),另一种来表示所要分配的空间在缓存区的位置。Nginx把缓存obj分成大的 (>=2048B)和小的(<2048B)。每次给大的obj分配一整个页,而把多个小obj存放在一个页中间,用bitmap等方法来表示 其占用情况。而小的obj又分为3种:小于128B,等于128B,大于128B且小于2048B。其中小于128B的obj需要在实际缓冲区额外分配 bitmap空间来表示内存使用情况(因为slab成员只有4个byte即32bit,一个缓存页4KB可以存放的obj超过32个,所以不能用slab 来表示),这样会造成一定的空间损失。等于或大于128B的obj因为可以用一个32bit的整形来表示其状态,所以就可以直接用slab成员。每次分配 的空间是2^n,最小是8byte,8,16,32,64,128,256,512,1024,2048。小于2^i且大于2^(i-1)的obj会被分 配一个2^i的空间,比如56byte的obj就会分配一个64byte的空间。

 

先看一下初始化slab pool的函数,在ngx_init_cycle()中调用的ngx_init_zone_pool()中被调用

C代码  收藏代码
  1. void ngx_slab_init(ngx_slab_pool_t *pool)  
  2. {  
  3.     //假设每个page是4KB  
  4.     //设置ngx_slab_max_size = 2048B。如果一个页要存放多个obj,则obj size要小于这个数值  
  5.     //设置ngx_slab_exact_size = 128B。分界是否要在缓存区分配额外空间给bitmap  
  6.     //ngx_slab_exact_shift = 7,即128的位表示  
  7.     //...  
  8.   
  9.     //pool->min_shift = 3  
  10.     //最小分配的空间是8byte  
  11.     pool->min_size = 1 << pool->min_shift;  
  12.   
  13.     //这些slab page是给大小为8,16,32,64,128,256,512,1024,2048byte的内存块  
  14.     //这些slab page的位置是在pool->pages的前面  
  15.     //初始化  
  16.     p = (u_char *) pool + sizeof(ngx_slab_pool_t);  
  17.     slots = (ngx_slab_page_t *) p;  
  18.     n = ngx_pagesize_shift - pool->min_shift;  
  19.     for (i = 0; i < n; i++) {  
  20.         slots[i].slab = 0;  
  21.         slots[i].next = &slots[i];  
  22.         slots[i].prev = 0;  
  23.     }  
  24.   
  25.     //跳过上面那些slab page  
  26.     p += n * sizeof(ngx_slab_page_t);  
  27.     //**计算这个空间总共可以分配的缓存页(4KB)的数量,每个页的overhead是一个slab page的大小  
  28.     //**这儿的overhead还不包括之后给<128B物体分配的bitmap的损耗  
  29.     pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));  
  30.     //把每个缓存页对应的slab page归0  
  31.     ngx_memzero(p, pages * sizeof(ngx_slab_page_t));  
  32.     //pool->pages指向slab page的头  
  33.     pool->pages = (ngx_slab_page_t *) p;  
  34.   
  35.     //初始化free,free.next是下次分配页时候的入口  
  36.     pool->free.prev = 0;  
  37.     pool->free.next = (ngx_slab_page_t *) p;  
  38.   
  39.     //更新第一个slab page的状态,这儿slab成员记录了整个缓存区的页数目  
  40.     pool->pages->slab = pages;  
  41.     pool->pages->next = &pool->free;  
  42.     pool->pages->prev = (uintptr_t) &pool->free;  
  43.   
  44.     //实际缓存区(页)的开头,对齐  
  45.     pool->start = (u_char *)ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t), ngx_pagesize);  
  46.   
  47.     //根据实际缓存区的开始和结尾再次更新内存页的数目  
  48.     m = pages - (pool->end - pool->start) / ngx_pagesize;  
  49.     if (m > 0) {  
  50.         pages -= m;  
  51.         pool->pages->slab = pages;  
  52.     }  
  53.   
  54.     //...  
  55. }  
 

下面来看一下需要分配缓存空间时调用的函数,由于是共享内存,所以在进程间需要用锁来保持同步

C代码  收藏代码
  1. void * ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size)  
  2. {  
  3.     //spinlock获取锁  
  4.     ngx_shmtx_lock(&pool->mutex);  
  5.     p = ngx_slab_alloc_locked(pool, size);  
  6.     //解锁  
  7.     ngx_shmtx_unlock(&pool->mutex);  
  8.     return p;  
  9. }  
 
C代码  收藏代码
  1. //返回的值是所要分配的空间在内存缓存区的位置  
  2. void * ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)  
  3. {  
  4.     //这儿假设page_size是4KB  
  5.     //如果是large obj, size >= 2048B  
  6.     if(...){  
  7.         //分配1个或多个内存页  
  8.         page = ngx_slab_alloc_pages(pool, (size + ngx_pagesize - 1) >> ngx_pagesize_shift);  
  9.         //返回指向内存缓存页的位置,这儿slab page的位置与所要返回的缓存页的位置是对应的  
  10.         p = (page - pool->pages) << ngx_pagesize_shift;  
  11.         p += (uintptr_t) pool->start;  
  12.   
  13.         //done, return p  
  14.         //...  
  15.     }  
  16.   
  17.     //较小的obj, size < 2048B  
  18.     //根据需要分配的size来确定在slots的位置,每个slot存放一种大小的obj的集合,如slots[0]表示8byte的空间,slots[3]表示64byte的空间  
  19.     //如果obj过小(<1B),slot的位置是1B空间的位置,即最小分配1B  
  20.     //...  
  21.   
  22.     //如果之前已经有此类大小obj且那个已经分配的内存缓存页还未满  
  23.     if(...){  
  24.         //小obj,size < 128B,更新内存缓存页中的bitmap,并返回待分配的空间在缓存的位置  
  25.         //...  
  26.   
  27.         //size == 128B,因为一个页可以放32个,用slab page的slab成员来标注每块内存的占用情况,不需要另外在内存缓存区分配bitmap,并返回待分配的空间在缓存的位置  
  28.         //...  
  29.   
  30.         //size > 128B,也是更新slab page的slab成员,但是需要预先设置slab的部分bit,因为一个页的obj数量小于32个,并返回待分配的空间在缓存的位置  
  31.         //...  
  32.     }  
  33.   
  34.     //此前没有此类大小的obj或者之前的页已经满了,分配一个新的页,page是新的页相应的slab page  
  35.     page = ngx_slab_alloc_pages(pool, 1);  
  36.   
  37.     //小obj,size < 128B,更新内存缓存页中的bitmap,并返回待分配的空间在缓存的位置(跳过bitmap的位置)  
  38.     //...  
  39.   
  40.     //size == 128B,更新slab page的slab成员(即页中的每个相同大小空间的占用情况),并返回待分配的空间在缓存的位置  
  41.     //...  
  42.   
  43.     //size > 128B,更新slab page的slab成员(即页中的每个相同大小空间的占用情况),并返回待分配的空间在缓存的位置  
  44.     //...  
  45. }  
 
C代码  收藏代码
  1. //返回一个slab page,这个slab page之后会被用来确定所需分配的空间在内存缓存的位置  
  2. static ngx_slab_page_t * ngx_slab_alloc_pages(...)  
  3. {  
  4.     //从pool->free.next开始,每次取(slab page) page = page->next  
  5.     for(;;){  
  6.         //本个slab page剩下的缓存页数目>=需要分配的缓存页数目N  
  7.         if(...){  
  8.             //更新从本个slab page开始往下第N个slab page的缓存页数目为本个slab page数目减去N  
  9.             //N为需要分配的缓存页数目  
  10.             //更新pool->free.next,下次从第N个slab page开始  
  11.             //...  
  12.   
  13.             //更新被分配的page slab中的第一个的slab成员,即页的个数和占用情况  
  14.             page->slab = pages | NGX_SLAB_PAGE_START;  
  15.             //...  
  16.   
  17.             //如果分配的页数N>1,更新后面page slab的slab成员为NGX_SLAB_PAGE_BUSY  
  18.             //...  
  19.   
  20.             return page;  
  21.         }  
  22.     }  
  23.   
  24.     //没有找到空余的页  
  25.     return NULL;  
  26. }  

 

附图

1. ngx_slab_alloc_pages图例:

 

 

2. 小物体bitmap图例:


 

3. slab page和缓存页的映射:




分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics