nginx中slab实现

来源:互联网 发布:万国数据上海 福利待遇 编辑:程序博客网 时间:2024/05/29 02:54

nginx中slab实现

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/2ngx_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;  }  
复制代码

 

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

原创粉丝点击