本来这一篇作为nginx系列的开头是不合适的,不过由于nginx进程框架自己的梳理还没
完成,这部分又刚好整理完了,就从这开始吧。
这儿谈的是nginx的slab的内存管理方式,这种方式的内存管理在nginx中,主要是与nginx
的共享内存协同使用的。nginx的slab管理与linux的slab管理相同的地方在于均是利用了内存
的缓存与对齐机制,slab内存管理中一些设计相当巧妙的地方,也有一些地方个人感觉设计
不是很完美,或许是作为nginx设计综合考虑的结果。
nginx slab实现中的一大特色就是大量的位操作,这部分操作很多是与slot分级数组相关的。

为方便描述下面做一些说明:
1.将整个slab的管理结构称slab pool.
2.将slab pool前部的ngx_slab_pool_t结构称slab header.
3.将管理内存分级的ngx_slab_page_t结构称slot分级数组.
4.将管理page页使用的ngx_slab_page_t结构称slab page管理结构.
5.将具体page页的存放位置称pages数组.
6.将把page分成的各个小块称为chunk.
7.将标记chunk使用的位图结构称为bitmap.

整个slab pool中有两个非常重要的结构,一是ngx_slab_pool_t,即slab header,如下示:

typedef struct {
ngx_atomic_t lock; 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;

其中最为重要的几个成员为:
min_size:指最小分割成的chunk的大小。
min_shift:指min_size对应的移位数。
*pages:指向slab page管理结构的开始位置。
free:空闲的slab page管理结构链表。
*start:pages数组的的起始位置。
*end:整个slab pool 的结束位置。
*addr:整个slab pool的开始位置。

另一个重要的结构是ngx_slab_page_t,slot分级数组与slab page管理结构都使用了这个结构,
如下示:

struct ngx_slab_page_s {
uintptr_t slab;
ngx_slab_page_t *next;
uintptr_t prev;
};

其中的成员说明如下示:
slab:slab为使用较为复杂的一个字段,有以下四种使用情况
  a.存储为些结构相连的pages的数目(slab page管理结构)
  b.存储标记chunk使用情况的bitmap(size = exact_size)
  c.存储chunk的大小(size < exact_size)
  d.存储标记chunk的使用情况及chunk大小(size > exact_size)
next:使用情况也会分情况处理,后面看。
prev:通常是组合使用在存储前一个位置及标记page的类型。

先来看下整个slab pool的结构,如下图示:

上面需要注意的几个地方:
1.由于内存对齐可能会导致slab pool中有部分未使用的内存区域。
2.由于内存对齐可能会导致pages数组的大小小于slab page管理结构的大小。
3.对于未使用的page管理链表其结点非常特殊,可以是由ngx_slab_page_t的数组构成
也可以是单个的ngx_slab_page_t.
4.pages数组中的page是与slab page管理结构一一对应的,虽然slab page有多的。

然后就是一些初始化数据,这儿仅考虑32位的情况。
ngx_pagesize = 4096;
ngx_pagesize_shift = 12;
ngx_slab_max_size = 2048;
ngx_slab_exact_size = 128;
ngx_slab_exact_shift = 7;
ngx_slab_min_size = 128;
ngx_slab_min_shift = 3;

先看slab 内存管理的初始化过程,具体的代码分析如下:

//slab空间的初始化函数
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 最大slab size的初始化*/
if (ngx_slab_max_size == ) {
ngx_slab_max_size = ngx_pagesize / ;
ngx_slab_exact_size = ngx_pagesize / ( * sizeof(uintptr_t));
for (n = ngx_slab_exact_size; n >>= ; ngx_slab_exact_shift++) {
/* void */
}
}
/**/
//计算最小的slab大小
pool->min_size = << pool->min_shift;
//跳过ngx_slab_page_t的空间,也即跳过slab header
p = (u_char *) pool + sizeof(ngx_slab_pool_t);
size = pool->end - p; //计算剩余可用空间的大小 ngx_slab_junk(p, size);
//进行slot分级数组的初始化
slots = (ngx_slab_page_t *) p;
n = ngx_pagesize_shift - pool->min_shift; //计算可分的级数,page_size为4kb时对应的shift为12,若
//最小可为8B,则shift为3,则对应可分为12-3,即8,16,32,64,
//128,256,512,1024,2048 9个分级。
for (i = ; i < n; i++) {
slots[i].slab = ;
slots[i].next = &slots[i]; //对应将每个next均初始化为自己
slots[i].prev = ;
}
//跳过slot分级数组区域
p += n * sizeof(ngx_slab_page_t);
//由于每一个page均对应一个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));
//初始化pages指针的位置
pool->pages = (ngx_slab_page_t *) p; pool->free.prev = ;
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);
//这个地方是进行对齐后的page调整,这个地方是我前面疑问的部分解决位置。
m = pages - (pool->end - pool->start) / ngx_pagesize;
if (m > ) {
pages -= m;
pool->pages->slab = pages;
} pool->log_ctx = &pool->zero;
pool->zero = '\0';
}

然后是内存申请过程:
step 1:根据申请size的大小,判断申请内存的方式:
  case 1:若大于ngx_slab_max_size则直接彩分配page的方式。
      调用ngx_slab_alloc_pages后跳转至step 5.
  case 2:若小于等于ngx_slab_max_size则根据size计算分级的级数。
      转step 2.
step 2:检查计算出的分级数对应的slot分级数组中是否存在可使用的页,
    若存在则转step 3,否则转step 4.
step 3:根据size的大小,进行不同的内存分配过程:
  case 1:size小于ngx_slab_exact_size时
    (1)遍历bitmap数组查找可用的chunk位置
    (2)完成chunk的标记
    (3)标记完成后检查chunk是否是对应的bitmap的最后一个被使用的,
     若是,则进步检查page中是否还存在未使用的chunk,若不存在则
     将page脱离出此slot分级数组的管理,标记page的类型为NGX_SLAB_SMALL.
    (4)计算申请到的chunk的内存起始地址,转至step 5.
  case 2:size等于ngx_slab_exact_size时
    (1)检查slab字段查找可用的chunk位置
    (2)同上
    (3)同上,不过page类型标记为NGX_SLAB_EXACT
    (4)同上
  case 3:size大于ngx_slab_exact_size时
    (1)从slab字段中提取出标记chunk使用的bitmap
    (2)同case 1 (1)
    (3)同case 2 (2)
    (4)同case 1 (3),不过page类型标记为NGX_SLAB_BIG
    (5)同case 1 (4)
step 4:调用ngx_slab_alloc_pages申请1页page,然后根据size情况完成page划分
    及bitmap的初始化标记。
  case 1:小于ngx_slab_exact_size时
    (1)计算需要使用的bitmap的个数
    (2)完成bitmap使用chunk的标记,同时标记即将分配出去的chunk
    (3)完成剩余的bitmap的初始化
    (4)设置page的类型为NGX_SLAB_SMALL
    (5)计算分配chunk的位置
  case 2:等于ngx_slab_exact_size时
    (1)完成即将分配的chunk的标记
    (2)设置page的类型为NGX_SLAB_EXACT
    (3)计算分配的chunk的位置
  case 3:
    (1)完成chunk的标记,及将chunk的大小同时存储于slab字段中
    (2)设置page的类型为NGX_SLAB_BIG
    (3)计算分配的chunk的位置
完成以上情况处理后,跳至step 5
step 5:返回得到空间。

下面看具体的代码:

 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;
//case 1:请求的size大于最大的slot的大小,直接以页的形式分配空间。
if (size >= ngx_slab_max_size) { ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, ,
"slab alloc: %uz", size);
//获取page
page = ngx_slab_alloc_pages(pool, (size + ngx_pagesize - )
>> ngx_pagesize_shift);
if (page) {
//计算具体的page的位置
p = (page - pool->pages) << ngx_pagesize_shift;
p += (uintptr_t) pool->start; } else {
p = ;
} goto done;
}
//case 2:请求的size小于等于2048,可用slot满足请求
if (size > pool->min_size) {
shift = ;
for (s = size - ; s >>= ; shift++) { /* void */ }
slot = shift - pool->min_shift; } else {
size = pool->min_size;
shift = pool->min_shift;
slot = ;
} ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, ,
"slab alloc: %uz slot: %ui", size, slot);
//取得分级数组的起始位置
slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t));
//获取对应的slot的用于取chunk的页
page = slots[slot].next;
//存在用于切割chunk的页
if (page->next != page) {
//case 2.1:请求的大小小于可exact切割的chunk大小,即128,需要占用page中前面的chunk来作为chunk使用状况的位图
if (shift < ngx_slab_exact_shift) { do {
//计算具体的page页的存放位置
p = (page - pool->pages) << ngx_pagesize_shift;
//得到bitmap起始存放的位置
bitmap = (uintptr_t *) (pool->start + p);
//计算对应shift大小的bitmap的个数
map = ( << (ngx_pagesize_shift - shift))
/ (sizeof(uintptr_t) * ); for (n = ; n < map; n++) {
//查找未使用的chunk.
if (bitmap[n] != NGX_SLAB_BUSY) {
//依次检查bitmap的各位,以得到未使用的chunk.
for (m = , i = ; m; m <<= , i++) {
if ((bitmap[n] & m)) {
continue;
}
//置使用标记
bitmap[n] |= m;
//计算找到的chunk的偏移位置。
i = ((n * sizeof(uintptr_t) * ) << shift)
+ (i << shift);
//当每个bitmap标示的chunk刚好使用完时,都会去检查是否还有chunk未使用
//若chunk全部使用完,则将当前的page脱离下来。
if (bitmap[n] == NGX_SLAB_BUSY) {
for (n = n + ; n < map; n++) {
if (bitmap[n] != NGX_SLAB_BUSY) {
//确认了还有未使用的chunk直接返回。
p = (uintptr_t) bitmap + i; goto done;
}
}
//page页中的chunk都使用完了,将page脱离
//与NGX_SLAB_PAGE_MASK的反&运算是为了去除之前设置的page类型标记以得到prev的地址。
prev = (ngx_slab_page_t *)
(page->prev & ~NGX_SLAB_PAGE_MASK);
prev->next = page->next;
page->next->prev = page->prev; page->next = NULL;
//置chunk的类型
page->prev = NGX_SLAB_SMALL;
} p = (uintptr_t) bitmap + i; goto done;
}
}
} page = page->next; } while (page); } else if (shift == ngx_slab_exact_shift) {
//请求的大小刚好为exact的大小,即128,这时slab仅做bitmap使用
do {
//直接比较slab看有空的chunk不。
if (page->slab != NGX_SLAB_BUSY) {
//逐位比较,查找chunk.
for (m = , i = ; m; m <<= , i++) {
if ((page->slab & m)) {
continue;
}
//置使用标记
page->slab |= m;
//检查page中的chunk是否使用完,使用完则做脱离处理
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 */
//case 2.3:申请的size大小128,但小于等于2048时。
//此时的slab同时存储bitmap及表示chunk大小的shift,高位为bitmap.
//获取bitmap的高位mask.
n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK);
n = << n;
n = ((uintptr_t) << n) - ;
mask = n << NGX_SLAB_MAP_SHIFT; do {
if ((page->slab & NGX_SLAB_MAP_MASK) != mask) {
//逐位查找空的chunk.
for (m = (uintptr_t) << NGX_SLAB_MAP_SHIFT, i = ;
m & mask;
m <<= , i++)
{
if ((page->slab & m)) {
continue;
} page->slab |= m;
//检查page中的chunk是否使用完
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);
}
} page = ngx_slab_alloc_pages(pool, ); if (page) {
if (shift < ngx_slab_exact_shift) {
//page用于小于128的chunk时,
//获取page的存放位置
p = (page - pool->pages) << ngx_pagesize_shift;
//获取bitmap的开始位置
bitmap = (uintptr_t *) (pool->start + p); s = << shift;
//计算chunk的个数
n = ( << (ngx_pagesize_shift - shift)) / / s; if (n == ) {
n = ;
}
//给bitmap占用的chunk置标记,同时对将要使用的chunk进行标记。
bitmap[] = ( << n) - ;
//计算要使用的bitmap的个数
map = ( << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * );
//从第二个开始初始化bitmap.
for (i = ; i < map; i++) {
bitmap[i] = ;
} page->slab = shift;
page->next = &slots[slot];
//设置page的类型,低位存储
page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL; slots[slot].next = page;
//计算申请的chunk的位置。
p = ((page - pool->pages) << ngx_pagesize_shift) + s * n;
p += (uintptr_t) pool->start; goto done; } else if (shift == ngx_slab_exact_shift) {
//chunk的大小正好为128时,此时处理很简单
page->slab = ;
page->next = &slots[slot];
//置chunk类型
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 */
//大于128时,slab要存储bitmap及表示chunk大小的shift.
page->slab = ((uintptr_t) << 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 = ; done: ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, , "slab alloc: %p", p); return (void *) p;
}

然后补充下,ngx_slab_alloc_pages函数的代码分析:

 //这个函数是用来申请page的,此函数的实现也为nginx slab在申请大内存的处理时留下了隐患。
static ngx_slab_page_t *
ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)
{
ngx_slab_page_t *page, *p;
//在slab page的管理页中查找,找到slab管理块中能够一次满足要求的slab,这和slab page的管理时不合并有关
for (page = pool->free.next; page != &pool->free; page = page->next) {
//找到了合适的slab
if (page->slab >= pages) {
//对于大于请求page数的情况,会将前pages个切分出去,page[pages]刚好为将
//pages个切分出去后,逗留下的第一个,正好作为构建新结点的第一个。下面
//其实是一个双向链表插入及删除节点时的操作
if (page->slab > 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 {
//恰好等于时,不用进行切分直接删除节点
p = (ngx_slab_page_t *) page->prev;
p->next = page->next;
page->next->prev = page->prev;
}
//修改page对应的状态
page->slab = pages | NGX_SLAB_PAGE_START;
page->next = NULL;
page->prev = NGX_SLAB_PAGE; if (--pages == ) {
return page;
}
//对于pages大于1的情况,还处理非第一个page的状态,修改为BUSY
for (p = page + ; pages; pages--) {
p->slab = NGX_SLAB_PAGE_BUSY;
p->next = NULL;
p->prev = NGX_SLAB_PAGE;
p++;
} return page;
}
} ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory"); return NULL;
}

下面是slab内存管理机制的释放过程分析:
step 1:判断异常情况,得到释放空间位于的page的slab 管理结构的位置及page的位置。
step 2:获取释放空间位于的page的类型,根据类型进行处理:
  case 1:NGX_SLAB_SMALL
    (1)从slab字段中取出chunk的大小
    (2)计算要释放的空间位于page中的chunk的偏移
    (3)计算对应chunk位于bitmap数组中的第几个bitmap
    (4)计算对应bitmap的地址
    (5)检查对应的chunk的bitmap中的标记,若为未释放标记则进行后面的处理,否则
      直接返回已经释放。
    (6)将释放空间的page重新置于对应分级数组的管理下
    (7)置释放标记,同时检查页中管理的chunk是否均为未使用,若全为,则调用
      ngx_slab_free_pages进行page的回收,即将其加入slab page的管理之下。
      否则返回。
  case 2:NGX_SLAB_EXACT
    (1)同case 1 (2)
    (2)同case 1 (5)
    (3)同case 1 (7)
  case 3:NGX_SLAB_BIG
    (1)从slab字段中提取出bitmap及chunk的大小
    (2)同case 1 (2)
    (3)同case 1 (5)
    (4)同case 1 (7)
   case 4:NGX_SLAB_PAGE
    好像是处理一些页相关的释放情况,不详细讨论
step 3:返回

下面看具体代码:

 void
ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)
{
size_t size;
uintptr_t slab, m, *bitmap;
ngx_uint_t n, type, slot, shift, map;
ngx_slab_page_t *slots, *page; ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, , "slab free: %p", p);
//判断异常情况
if ((u_char *) p < pool->start || (u_char *) p > pool->end) {
ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool");
goto fail;
}
//计算释放的page的偏移
n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;
//获取对应slab_page管理结构的位置
page = &pool->pages[n];
slab = page->slab;
//获取page的类型
type = page->prev & NGX_SLAB_PAGE_MASK; switch (type) {
//page类型为小于128时。
case NGX_SLAB_SMALL:
//此时chunk的大小存放于slab中。
shift = slab & NGX_SLAB_SHIFT_MASK;
//计算chunk的大小
size = << shift; if ((uintptr_t) p & (size - )) {
goto wrong_chunk;
}
//这段特别巧妙,由于前面对页进行了内存对齐的处理,因此下面的式子可直接
//求出p位于的chunk偏移,即是page中的第几个chunk.
n = ((uintptr_t) p & (ngx_pagesize - )) >> shift;
//计算chunk位于bitmap管理的chunk的偏移,注意对2的n次方的取余操作的实现。
m = (uintptr_t) << (n & (sizeof(uintptr_t) * - ));
//计算p指向的chunk位于第几个bitmap中。
n /= (sizeof(uintptr_t) * );
//计算bitmap的起始位置
bitmap = (uintptr_t *) ((uintptr_t) p & ~(ngx_pagesize - ));
//判断是否处于free状态。
if (bitmap[n] & m) {
//将page插入到对应slot分级数组管理的slab链表中,位于头部
if (page->next == NULL) {
slots = (ngx_slab_page_t *)
((u_char *) pool + sizeof(ngx_slab_pool_t));
slot = shift - pool->min_shift; page->next = slots[slot].next;
slots[slot].next = page; page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;
page->next->prev = (uintptr_t) page | NGX_SLAB_SMALL;
}
//置释放标记
bitmap[n] &= ~m;
//计算chunk的个数
n = ( << (ngx_pagesize_shift - shift)) / / ( << shift); if (n == ) {
n = ;
}
//检查首个bitmap对bitmap占用chunk的标记情况。
if (bitmap[] & ~(((uintptr_t) << n) - )) {
goto done;
}
//计算bitmap的个数
map = ( << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * ); for (n = ; n < map; n++) {
if (bitmap[n]) {
goto done;
}
}
//如果释放后page中没有在使用的chunk,则进行进一步的回收,改用slab_page进行管理
ngx_slab_free_pages(pool, page, ); goto done;
} goto chunk_already_free; case NGX_SLAB_EXACT: m = (uintptr_t) <<
(((uintptr_t) p & (ngx_pagesize - )) >> ngx_slab_exact_shift);
size = ngx_slab_exact_size; if ((uintptr_t) p & (size - )) {
goto wrong_chunk;
} if (slab & m) {
if (slab == NGX_SLAB_BUSY) {
slots = (ngx_slab_page_t *)
((u_char *) pool + sizeof(ngx_slab_pool_t));
slot = ngx_slab_exact_shift - pool->min_shift; page->next = slots[slot].next;
slots[slot].next = page; page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;
page->next->prev = (uintptr_t) page | NGX_SLAB_EXACT;
} page->slab &= ~m; if (page->slab) {
goto done;
} ngx_slab_free_pages(pool, page, ); goto done;
} goto chunk_already_free; case NGX_SLAB_BIG: shift = slab & NGX_SLAB_SHIFT_MASK;
size = << shift; if ((uintptr_t) p & (size - )) {
goto wrong_chunk;
} m = (uintptr_t) << ((((uintptr_t) p & (ngx_pagesize - )) >> shift)
+ NGX_SLAB_MAP_SHIFT); if (slab & m) { if (page->next == NULL) {
slots = (ngx_slab_page_t *)
((u_char *) pool + sizeof(ngx_slab_pool_t));
slot = shift - pool->min_shift; page->next = slots[slot].next;
slots[slot].next = page; page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;
page->next->prev = (uintptr_t) page | NGX_SLAB_BIG;
} page->slab &= ~m; if (page->slab & NGX_SLAB_MAP_MASK) {
goto done;
} ngx_slab_free_pages(pool, page, ); goto done;
} goto chunk_already_free; case NGX_SLAB_PAGE: if ((uintptr_t) p & (ngx_pagesize - )) {
goto wrong_chunk;
} if (slab == NGX_SLAB_PAGE_FREE) {
ngx_slab_error(pool, NGX_LOG_ALERT,
"ngx_slab_free(): page is already free");
goto fail;
} if (slab == NGX_SLAB_PAGE_BUSY) {
ngx_slab_error(pool, NGX_LOG_ALERT,
"ngx_slab_free(): pointer to wrong page");
goto fail;
} n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;
size = slab & ~NGX_SLAB_PAGE_START; ngx_slab_free_pages(pool, &pool->pages[n], size); ngx_slab_junk(p, size << ngx_pagesize_shift); return;
} /* not reached */ return; done: ngx_slab_junk(p, size); return; wrong_chunk: ngx_slab_error(pool, NGX_LOG_ALERT,
"ngx_slab_free(): pointer to wrong chunk"); goto fail; chunk_already_free: ngx_slab_error(pool, NGX_LOG_ALERT,
"ngx_slab_free(): chunk is already free"); fail: return;
}

补充ngx_slab_free_pages的代码分析:

 static void
ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,
ngx_uint_t pages)
{
ngx_slab_page_t *prev;
//计算结点后部跟的page的数目
page->slab = pages--;
//对跟的page的page管理结构slab_page进行清空。
if (pages) {
ngx_memzero(&page[], pages * sizeof(ngx_slab_page_t));
}
//如果page后面还跟有节点,则将其连接至page的前一个结点。
if (page->next) {
prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK);
prev->next = page->next;
page->next->prev = page->prev;
}
//将page重新归于,slab_page的管理结构之下。放于管理结构的头部。
page->prev = (uintptr_t) &pool->free;
page->next = pool->free.next; page->next->prev = (uintptr_t) page; pool->free.next = page;
}

然后再看下slot分级数组对于page的管理:
初始化:初始化时,各个slot将未分配具体的page.
申请时:初始申请时没有page空间可用,然后会像slab page管理结构申请page空间,完成
page的划分chunk及bitmap的初始后,会分配一块chunk以供使用。对于再次申请时
会直接从page中取可用的chunk,当page时无可用的chunk时,此page页会暂时脱离
slot分级数组的管理,即将其从对应链表中删除。
释放时:释放时完成空间的回收标记后,会将page插入到对应的slot管理链表的头部,然后
会进一步检查些page是否全部chunk均未使用,若是,则进步回收此page将其置于
slab page管理结构的管理之下。
具体如下图示:
BEGIN:

AFTER:


..
最后就是对于slab管理机制对于使用一段时间后,对于大内存申请的处理会大概率返回失败
的情况分析。

主要原因在于ngx_slab_free_pages函数里面,从函数中看出,每次回收页到slab page的管理
结构中时只会对page进行加入链表的操作,而没有如同伙伴算法的结点合并操作,这样经由
ngx_slab_alloc_pages申请大内存时,在查找一个结点拥有符合要求的page数目时,将不能
得到一个满足要求的节点,因为使用一段时间后,可能slab page管理结构中的各个结点均会
成为小的数目page的组合。导致申请大的内存失败。

转载请注明出处:http://www.cnblogs.com/doop-ymc/p/3412572.html

nginx slab内存管理的更多相关文章

  1. Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制

    Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches   Slab内存管理机制 SLUB内存管理机制 http://w ...

  2. nginx的内存管理

    先来看内存池的实现,nginx的内存池实现的非常简单. 这里内存池的一些图表可以看老朱同学的slides : http://blog.zhuzhaoyuan.com/2009/09/nginx-int ...

  3. 利用 gperftools 对nginx mysql 内存管理 性能优化

    利用 gperftools 对nginx 与 mysql  进行 内存管理  性能优化 降低负载. Gperftools 是由谷歌开发.官方对gperftools 的介绍为: These tools ...

  4. 关于linux kernel slab内存管理的一点思考

    linux kernel 内存管理是个很大的话题,这里记录一点个人关于slab模块的一点思考总结. 有些书把slab介绍成高速缓存,这会让人和cache,特别是cpu cache混淆,造成误解.sla ...

  5. Nginx源码研究四:NGINX的内存管理

    关于nginx的内存使用,我们先看代码,下面是nginx_cycle.c中对全局数据结构cycle的初始化过程 pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, ...

  6. nginx源码分析—内存池结构ngx_pool_t及内存管理

    Content 0. 序 1. 内存池结构 1.1 ngx_pool_t结构 1.2 其他相关结构 1.3 ngx_pool_t的逻辑结构 2. 内存池操作 2.1 创建内存池 2.2 销毁内存池 2 ...

  7. 结合源码看nginx-1.4.0之nginx内存管理详解

    目录 0. 摘要 1. nginx内存结构设计 2. nginx内存数据结构 3. nginx内存管理原理 4. 一个简单的内存模型 5. 小结 6. 参考资料 0. 摘要 内存管理,是指软件运行时对 ...

  8. Memcached源码分析——内存管理

    注:这篇内容极其混乱 推荐学习这篇博客.博客的地址:http://kenby.iteye.com/blog/1423989 基本元素item item是Memcached中记录存储的基本单元,用户向m ...

  9. RT-thread内核之小内存管理算法

     一.动态内存管理 动态内存管理是一个真实的堆(Heap)内存管理模块,可以在当前资源满足的情况下,根据用户的需求分配任意大小的内存块.而当用户不需要再使用这些内存块时,又可以释放回堆中供其他应用分配 ...

随机推荐

  1. redis数据类型:sorted sets类型及操作

    sorted sets类型及操作: sorted set是set的一个升级版本,它是在set的基础上增加了一个顺序 属性,这一属性在添加修改元素的时候可以指定,每次指定后,zset会 自动重新按新的值 ...

  2. 自定义函数动态执行SQL语句

    Oracle 动态SQL有两种写法:用 DBMS_SQL 或 execute immediate,建议使用后者. DDL 和 DML Sql代码 收藏代码 /*** DDL ***/ begin EX ...

  3. CSS的标签类型

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  4. 封装一个Ajax工具函数

    /*封装一个ajax工具函数*/ window.$ = {}; /*通过$定义一个ajax函数*/ /* * 1. type   string   请求的方式  默认是get * 2. url     ...

  5. JavaScript 构造函数 prototype属性和_proto_和原型链 constructor属性 apply(),call()和bind() 关键字this

    1.构造函数: 通常构造函数首字母需要大写,主要是为了区别ECMAScript的其它函数.(高程三 P145) 构造函数与其他函数的唯一区别,就在于调用它们的方式不同.只要通过new来调用,任何函数都 ...

  6. javaWEB总结(3):ServletConfig对象

    1.首先找到ServletConfig的API: ServletConfig封装了servlet的配置信息,并且可以获取servletContext对象. ServletConfig共有四个方法: 1 ...

  7. shape的使用

    android在布局边缘位置处理圆角的两个办法: 1),一个是直接让美工切一张带有圆角的图片. 2),使用shape来解决. 第一种不在赘述,主要讲一下第二中方法来实现. 上边缘出现圆角,下边缘正常的 ...

  8. sd卡文件操作

    1. 得到存储设备的目录:/SDCARD(一般情况下) SDPATH=Environment.getExternalStorageDirectory()+"/"; 2. 判断SD卡 ...

  9. win7下将dll文件的打开方式改回系统默认

    打开注册表,定位到HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.dll把除OpenWit ...

  10. git(创建,提交,回退)

    创建版本库 2334次阅读 什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改.删除,Git都能跟踪,以 ...