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

本文介绍了Nginx的内存缓存机制,特别是slab pool如何实现内存缓存。核心结构ngx_slab_pool_t包含了内存缓存的状态、位置映射等信息。Nginx将缓存对象分为大对象和小对象,其中小对象根据大小进一步细分。文章详细讲解了ngx_slab_page_t结构和分配内存的函数,如ngx_slab_alloc(),并探讨了初始化slab pool的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:http://bollaxu.iteye.com/blog/875163

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和缓存页的映射:





二:

http://www.cnblogs.com/fll369/archive/2012/11/26/2789704.html

slab的一些结构体:

复制代码
typedef struct {  
    ngx_atomic_t      lock; // 锁,因为slab在nginx中一般配合共享内存使用  
  
    size_t            min_size; // 分配空间的最小值  
    size_t            min_shift;    // 该最小值对应的移位数  
  
    ngx_slab_page_t  *pages; // 页数组  
    ngx_slab_page_t   free; // 空闲的页  
  
    u_char           *start;    // 分配地址开始地址  
    u_char           *end;    
  
    ngx_shmtx_t       mutex;  
  
    u_char           *log_ctx;  
    u_char            zero;  
  
    void             *data;  
    void             *addr;  
} ngx_slab_pool_t;  
  
// 页结构体  
struct ngx_slab_page_s {  
    uintptr_t         slab;     // 保存当前页的一些信息  
    ngx_slab_page_t  *next;// 下一个  
    uintptr_t         prev;// 上一个  
};  
复制代码

slab的函数调用:

复制代码
// 初始化slab池  
void ngx_slab_init(ngx_slab_pool_t *pool);  
// 未加锁的  
void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size);  
// 在调用前已加锁,分配指定大小空间  
void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size);  
void ngx_slab_free(ngx_slab_pool_t *pool, void *p);  
// 释放空间  
void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);  
复制代码

关于slab的使用,我们在介绍nginx中共享内存的时候再去介绍吧,我们只需要知道在进程初始化时,ngx_init_cycle函数会调用ngx_init_zone_pool来初始化共享内存,然后在ngx_init_zone_pool函数中会调用ngx_slab_init来初始化slab内存池。随后,在进程中,我们就可以调用alloc与free来对共享内存进行操作了。

对于64位与32位系统,nginx里面默认的值是不一样的,我们看到数字可能会更好理解一点,所以我们就以32位来看,用实际的数字来说话!

 

// 页大小
ngx_pagesize: 4096
// 页大小对应的移位数
ngx_pagesize_shift: 12
// slab的一次最大分配空间,默认为pagesize/2
ngx_slab_max_size: 2048
// slab精确分配大小,这个是一个分界点,通常是4096/32,为什么会这样,我们后面会有介绍
ngx_slab_exact_size: 128
// slab精确分配大小对应的移位数
ngx_slab_exact_shift: 7

ngx_slab_exact_size这个时依赖slab的分配算法.它的值是这样来的.4096/32,2048是slab页大小,而32是一个int的位数,最后的值是128。
why? 我们在分配时,在一页中,我们可以将这一页分成多个块,而某个块需要标记是否被分配,而一页空间正好被分成32个128字节大小的块,于是我们可以用一个int的32位表示这块的使用情况,而此时,我们是使用ngx_slab_page_s结构体中的slab成员来表示块的使用情况的。另外,在分配大于128与小于128时,表示块的占用情况有所有同
复制代码
// 初始化  
void  
ngx_slab_init(ngx_slab_pool_t *pool)  
{  
    u_char           *p;  
    size_t            size;  
    ngx_int_t         m;  
    ngx_uint_t        i, n, pages;  
    ngx_slab_page_t  *slots;  
  
    /* STUB */  
    if (ngx_slab_max_size == 0) {  
        // 最大分配空间为页大小的一半  
        ngx_slab_max_size = ngx_pagesize / 2;  
        // 精确分配大小,8为一个字节的位数,sizeof(uintptr_t)为一个uintptr_t的字节,我们后面会根据这个size来判断使用不同的分配算法  
        ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));  
        // 计算出此精确分配的移位数  
        for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {  
            /* void */  
        }  
    }  
    /**/  
  
    pool->min_size = 1 << pool->min_shift;  
  
    // p 指向slot数组  
    p = (u_char *) pool + sizeof(ngx_slab_pool_t);  
    size = pool->end - p;  
  
    // 将开始的size个字节设置为0  
    ngx_slab_junk(p, size);  
  
    // 某一个大小范围内的页,放到一起,具有相同的移位数  
    slots = (ngx_slab_page_t *) p;  
    // 最大移位数,减去最小移位数,得到需要的slot数量  
    // 默认为8  
    n = ngx_pagesize_shift - pool->min_shift;  
  
    // 初始化各个slot  
    for (i = 0; i < n; i++) {  
        slots[i].slab = 0;  
        slots[i].next = &slots[i];  
        slots[i].prev = 0;  
    }  
  
    // 指向页数组  
    p += n * sizeof(ngx_slab_page_t);  
  
    // 计算出当前内存空间可以放下多少个页,此时的计算没有进行对齐,在后面会进行调整  
    pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));  
  
    ngx_memzero(p, pages * sizeof(ngx_slab_page_t));  
  
    pool->pages = (ngx_slab_page_t *) p;  
  
    pool->free.prev = 0;  
    pool->free.next = (ngx_slab_page_t *) p;  
  
    pool->pages->slab = pages;  
    pool->pages->next = &pool->free;  
    pool->pages->prev = (uintptr_t) &pool->free;  
  
    // 计算出对齐后的返回内存的地址  
    pool->start = (u_char *)  
                  ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t),  
                                 ngx_pagesize);  
  
    // 用于判断我们对齐后的空间,是否需要进行调整  
    m = pages - (pool->end - pool->start) / ngx_pagesize;  
    // 说明之前是没有对齐过的,由于对齐之后,最后那一页,有可能不够一页,所以要去掉那一块  
    if (m > 0) {  
        pages -= m;  
        pool->pages->slab = pages;  
    }  
  
    pool->log_ctx = &pool->zero;  
    pool->zero = '\0';
}  
复制代码
复制代码
void *  
ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)  
{  
    size_t            s;  
    uintptr_t         p, n, m, mask, *bitmap;  
    ngx_uint_t        i, slot, shift, map;  
    ngx_slab_page_t  *page, *prev, *slots;  
  
    // 如果超出slab最大可分配大小,即大于2048,则我们需要计算出需要的page数,  
    // 然后从空闲页中分配出连续的几个可用页  
    if (size >= ngx_slab_max_size) {  
  
        // 计算需要的页数,然后分配指针页数  
        page = ngx_slab_alloc_pages(pool, (size + ngx_pagesize - 1)  
                                          >> ngx_pagesize_shift);  
        if (page) {  
            // 由返回page在页数组中的偏移量,计算出实际数组地址的偏移量  
            p = (page - pool->pages) << ngx_pagesize_shift;  
            // 计算出实际的数据地址  
            p += (uintptr_t) pool->start;  
  
        } else {  
            p = 0;  
        }  
  
        goto done;  
    }  
  
    // 如果小于2048,则启用slab分配算法进行分配  
  
    // 计算出此size的移位数以及此size对应的slot以及移位数  
    if (size > pool->min_size) {  
        shift = 1;  
        // 计算移位数  
        for (s = size - 1; s >>= 1; shift++) { /* void */ }  
        // 由移位数得到slot  
        slot = shift - pool->min_shift;  
  
    } else {  
        // 小于最小可分配大小的都放到一个slot里面  
        size = pool->min_size;  
        shift = pool->min_shift;  
        // 因为小于最小分配的,所以就放在第一个slot里面  
        slot = 0;  
    }  
  
    slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t));  
    // 得到当前slot所占用的页  
    page = slots[slot].next;  
  
    // 找到一个可用空间  
    if (page->next != page) {  
  
        // 分配大小小于128字节时的算法,看不懂的童鞋可以先看等于128字节的情况  
        // 当分配空间小于128字节时,我们不可能用一个int来表示这些块的占用情况  
        // 此时,我们就需要几个int了,即一个bitmap数组  
        // 我们此时没有使用page->slab,而是使用页数据空间的开始几个int空间来表示了  
        // 看代码  
  
        if (shift < ngx_slab_exact_shift) {  
  
            do {  
                // 得到页数据部分  
                p = (page - pool->pages) << ngx_pagesize_shift;  
                // 页的开始几个int大小的空间来存放位图数据  
                bitmap = (uintptr_t *) (pool->start + p);  
  
                // 当前页,在当前size下可分成map*32个块  
                // 我们需要map个int来表示这些块空间  
                map = (1 << (ngx_pagesize_shift - shift))  
                          / (sizeof(uintptr_t) * 8);  
  
                for (n = 0; n < map; n++) {  
  
                    if (bitmap[n] != NGX_SLAB_BUSY) {  
  
                        for (m = 1, i = 0; m; m <<= 1, i++) {  
                            if ((bitmap[n] & m)) {  
                                // 当前位表示的块已被使用了  
                                continue;  
                            }  
  
                            // 设置已占用  
                            bitmap[n] |= m;  
  
                            i = ((n * sizeof(uintptr_t) * 8 ) << shift)  
                                + (i << shift);  
  
                            // 如果当前bitmap所表示的空间已都被占用,就查找下一个bitmap  
                            if (bitmap[n] == NGX_SLAB_BUSY) {  
                                for (n = n + 1; n < map; n++) {  
                                    // 找到下一个还剩下空间的bitmap  
                                     if (bitmap[n] != NGX_SLAB_BUSY) {  
                                         p = (uintptr_t) bitmap + i;  
  
                                         goto done;  
                                     }  
                                }  
  
                                // 剩下所有的bitmap都被占用了,表明当前的页已完全被使用了,把当前页从链表中删除  
                                prev = (ngx_slab_page_t *)  
                                            (page->prev & ~NGX_SLAB_PAGE_MASK);  
                                prev->next = page->next;  
                                page->next->prev = page->prev;  
  
                                page->next = NULL;  
                                // 小内存分配  
                                page->prev = NGX_SLAB_SMALL;  
                            }  
  
                            p = (uintptr_t) bitmap + i;  
  
                            goto done;  
                        }  
                    }  
                }  
  
                page = page->next;  
  
            } while (page);  
  
        } else if (shift == ngx_slab_exact_shift) {  
            // 如果分配大小正好是128字节,则一页可以分成32个块,我们可以用一个int来表示这些个块的使用情况  
            // 这里我们使用page->slab来表示这些块的使用情况,当所有块被占用后,该值就变成了0xffffffff,即NGX_SLAB_BUSY  
            // 表示该块都被占用了  
  
            do {  
                // 当前页可用  
                if (page->slab != NGX_SLAB_BUSY) {  
  
                    for (m = 1, i = 0; m; m <<= 1, i++) {  
                        // 如果当前位被使用了,就继续查找下一块  
                        if ((page->slab & m)) {  
                            continue;  
                        }  
  
                        // 设置当前为已被使用  
                        page->slab |= m;  
  
                        // 最后一块也被使用了,就表示此页已使用完  
                        if (page->slab == NGX_SLAB_BUSY) {  
                            // 将当前页从链表中移除  
                            prev = (ngx_slab_page_t *)  
                                            (page->prev & ~NGX_SLAB_PAGE_MASK);  
                            prev->next = page->next;  
                            page->next->prev = page->prev;  
  
                            page->next = NULL;  
                            // 标识使用类型,精确  
                            page->prev = NGX_SLAB_EXACT;  
                        }  
  
                        p = (page - pool->pages) << ngx_pagesize_shift;  
                        p += i << shift;  
                        p += (uintptr_t) pool->start;  
  
                        goto done;  
                    }  
                }  
  
                // 查找下一页  
                page = page->next;  
  
            } while (page);  
  
        } else { /* shift > ngx_slab_exact_shift */  
            // 当需要分配的空间大于128时,我们可以用一个int的位来表示这些空间  
            //所以我们依然采用跟等于128时类似的情况,用page->slab来表示  
            // 但由于 大于128的情况比较多,移位数分别为8、9、10、11这些情况  
            // 对于一个页,我们如何来知道这个页的分配大小呢?  
            // 而我们知道,最小我们只需要使用16位即可表示这些空间了,即分配大小为256~512时  
            // 那么我们采用高16位来表示这些空间的占用情况  
            // 而最低位,我们也利用起来,表示此页的分配大小,即保存移位数  
            // 比如我们分配256,当分配第一个空间时,此时的page->slab位图情况是:0x0001008  
            // 那分配下一空间就是0x0003008了,当为0xffff008时,就分配完了  
            // 看代码  
  
            // page->slab & NGX_SLAB_SHIFT_MASK 即得到最低一位的值,其实就是当前页的分配大小的移位数  
            // ngx_pagesize_shift减掉后,就是在一页中标记这些块所需要的移位数,也就是块数对应的移位数  
            n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK);  
            // 得到一个页面所能放下的块数  
            n = 1 << n;  
            // 得到表示这些块数都用完的bitmap,用现在是低16位的  
            n = ((uintptr_t) 1 << n) - 1;  
            // 将低16位转换成高16位,因为我们是用高16位来表示空间地址的占用情况的  
            mask = n << NGX_SLAB_MAP_SHIFT;  
  
            do {  
                // 判断高16位是否全被占用了  
                if ((page->slab & NGX_SLAB_MAP_MASK) != mask) {  
  
                    // NGX_SLAB_MAP_SHIFT 为移位偏移, 得到0x10000  
                    for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;  
                         m & mask;  
                         m <<= 1, i++)  
                    {  
                        // 当前块是否被占用  
                        if ((page->slab & m)) {  
                            continue;  
                        }  
  
                        // 将当前位设置成1  
                        page->slab |= m;  
  
                        // 当前页是否完全被占用完  
                        if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {  
                            prev = (ngx_slab_page_t *)  
                                            (page->prev & ~NGX_SLAB_PAGE_MASK);  
                            prev->next = page->next;  
                            page->next->prev = page->prev;  
  
                            page->next = NULL;  
                            page->prev = NGX_SLAB_BIG;  
                        }  
  
                        p = (page - pool->pages) << ngx_pagesize_shift;  
                        p += i << shift;  
                        p += (uintptr_t) pool->start;  
  
                        goto done;  
                    }  
                }  
  
                page = page->next;  
  
            } while (page);  
        }  
    }  
  
    // 如果当前slab对应的page中没有空间可分配了,则重新从空闲page中分配一个页  
    page = ngx_slab_alloc_pages(pool, 1);  
  
    if (page) {  
        if (shift < ngx_slab_exact_shift) {  
            // 小于128时  
            p = (page - pool->pages) << ngx_pagesize_shift;  
            bitmap = (uintptr_t *) (pool->start + p);  
  
            // 需要的空间大小  
            s = 1 << shift;  
            n = (1 << (ngx_pagesize_shift - shift)) / 8 / s;  
  
            if (n == 0) {  
                n = 1;  
            }  
  
            bitmap[0] = (2 << n) - 1;  
  
            // 需要使用的  
            map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);  
  
            for (i = 1; i < map; i++) {  
                bitmap[i] = 0;  
            }  
  
            page->slab = shift;  
            page->next = &slots[slot];  
            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;  
  
            slots[slot].next = page;  
  
            p = ((page - pool->pages) << ngx_pagesize_shift) + s * n;  
            p += (uintptr_t) pool->start;  
  
            goto done;  
  
        } else if (shift == ngx_slab_exact_shift) {  
  
            // 第一块空间被占用  
            page->slab = 1;  
            page->next = &slots[slot];  
            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;  
  
            slots[slot].next = page;  
  
            p = (page - pool->pages) << ngx_pagesize_shift;  
            p += (uintptr_t) pool->start;  
  
            goto done;  
  
        } else { /* shift > ngx_slab_exact_shift */  
  
            // 低位表示存放数据的大小  
            page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift;  
            page->next = &slots[slot];  
            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;  
  
            slots[slot].next = page;  
  
            p = (page - pool->pages) << ngx_pagesize_shift;  
            p += (uintptr_t) pool->start;  
  
            goto done;  
        }  
    }  
  
    p = 0;  
  
done:  
  
    return (void *) p;  
}  
复制代码
复制代码
/ 分配指针个数的页  
static ngx_slab_page_t *  
ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)  
{  
    ngx_slab_page_t  *page, *p;  
  
    for (page = pool->free.next; page != &pool->free; page = page->next) {  
  
        if (page->slab >= pages) {  
  
            // 当前可以分配  
            if (page->slab > pages) {  
                // 空闲的pages大于需要分配的pages  
  
                // 减少  
                page[pages].slab = page->slab - pages;  
                // 这几个页就分配出去了哦!  
                page[pages].next = page->next;  
                page[pages].prev = page->prev;  
  
                p = (ngx_slab_page_t *) page->prev;  
                p->next = &page[pages];  
                page->next->prev = (uintptr_t) &page[pages];  
  
            } else {  
                // slab == pages  
                // 正好分配完  
  
                // 设置free链表  
                p = (ngx_slab_page_t *) page->prev;  
                p->next = page->next;  
                page->next->prev = page->prev;  
            }  
  
            page->slab = pages | NGX_SLAB_PAGE_START;  
            page->next = NULL;  
            page->prev = NGX_SLAB_PAGE;  
  
            if (--pages == 0) {  
                return page;  
            }  
  
            for (p = page + 1; pages; pages--) {  
                p->slab = NGX_SLAB_PAGE_BUSY;  
                p->next = NULL;  
                p->prev = NGX_SLAB_PAGE;  
                p++;  
            }  
  
            return page;  
        }  
    }  
  
    return NULL;  
}  
复制代码


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值