访问这里,获取更多原创内容。数组
说明:本系列的文章基于Nginx-1.5.0版本代码。数据结构
Nginx slab分配器用于管理和分配小于一页的内存申请,但实际上大于一页的内存分配也是统一实现的, 具体代码在core/ngx_slab.c文件中,对应的头文件是core/ngx_slab.h。函数
ngx_slab.h头文件中定义了两个重要的数据结构:布局
ngx_slab_pool_t;/*整个内存区的管理结构*/ ngx_slab_page_t;/*用于表示page页内存管理单元和slot分级管理单元*/
同时还声明了对外提供的几个函数原型,分别是:ui
用于初始化内存管理结构的:spa
void ngx_slab_init(ngx_slab_pool_t *pool);
用于内存分配的:.net
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_calloc(ngx_slab_pool_t *pool, size_t size); void *ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size); /*alloc 和 calloc的区别在因而否在分配的同时将内存清零*/
用于内存释放的:code
void ngx_slab_free(ngx_slab_pool_t *pool, void *p); void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);
这一次咱们先从ngx_slab_init()函数开始,看看slab分配器的基本结构是怎么样的。blog
因为slab管理和分配的内存是以2的幂次方为基准向上取整的,也就是说,若是你想申请一块20bytes的内存,那么就向上取整到32bytes。 内存
ngx_slab.c的实现中将内存分配分为两大类:
基于页(page)的内存分配,由page页内存管理单元来进行管理,其实现相对简单,由于整个内存池的基本结构就是以页为单位进行划分的。
基于块(chunk)的内存分配,将一页划分为若干块,实现相对复杂,除了page页内存管理单元外还引入了分级内存管理单元(slot数组)来共同管理;
实际上,page页内存管理单元和slot分级管理单元都是由ngx_slab_page_t结构来表示的,slot分级管理数组紧跟在ngx_slab_pool_t结构以后,page页内存管理数组又紧跟在slot分级管理数组以后。
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) { /*slab分配器最大分配大小(slab分配器用于分配小于一页的内存申请,因为实际会以2的幂次方为基准向上取整,所以超过1/2页大小的内存申请也被取整为1页)*/ ngx_slab_max_size = ngx_pagesize / 2; /*当使用bit位来标记块的使用状况时,若是想用一个uintptr_t类型的数来标记一整页中的全部块,则须要将一页分为(8 * sizeof(uintptr_t)个块,每块大小为ngx_slab_exact_size*/ ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t)); /*根据ngx_slab_exact_size计算对应的块大小移位ngx_slab_exact_shift*/ for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) { /* void */ } } /**/ /*slab的最小分配单元,当申请的内存比min_size还小时,则取整到min_size; 一般min_shift=3,则min_size=8*/ pool->min_size = 1 << pool->min_shift; p = (u_char *) pool + sizeof(ngx_slab_pool_t); size = pool->end - p;/*除ngx_slab_pool_t以外的剩余空间大小*/ ngx_slab_junk(p, size); /*slot分级管理数组的起始地址*/ slots = (ngx_slab_page_t *) p; /*从最小块大小到页大小之间的分级数*/ n = ngx_pagesize_shift - pool->min_shift; 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); /*每个实际的page页都对应一个页内存管理单元(后面会看到,反过来则不成立),这里会有疑问为何size上没有减去slot分级数组占用的空间,下面会说明*/ 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; /*页内存管理单元中的slab字段记录了其后跟随的连续空闲内存页数*/ pool->pages->slab = pages; pool->pages->next = &pool->free; pool->pages->prev = (uintptr_t) &pool->free; /*将实际的page页起始地址对齐到pagesize*/ pool->start = (u_char *) ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t), ngx_pagesize); /*对齐后剩余的内存空间可能不足pages个页,须要进行调整;另外也能够看出实际的内存页数可能会少于page页管理单元的数目,多余的几个就空闲在最后好了,这也是为何上面计算pages时size并无减去slot分级数组大小的缘由,由于一切都是由最终对齐后的内存空间大小决定的,因此前面也就没必要要求那么精确了*/ 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'; }
初始化完成以后,整个内存结构布局就是这个样子滴:
图中的各个标记基本保持与ngx_slab_init()函数中一致, 其中,N = pages - m,即通过对齐调整后的实际内存页数。
有了这个图作铺垫,咱们再讨论page页的分配时就容易多了。