访问这里,获取更多原创内容。 算法
说明:本系列的文章基于Nginx-1.5.0版本代码。函数
在上一篇中已经介绍了Nginx slab分配器的基本原理和内存空间布局,如今咱们将在此基础上引入“基于页的内存分配”的相关内容。之因此这样安排是由于它的实现相对于“基于块的内存分配”要简单许多,同时它又是“基于块的内存分配”的基础,以它为突破口怎么看都是最好的选择:)布局
在”基于页的内存分配“流程中只须要用到”page页内存管理单元“,而不涉及”分级内存管理单元“,为了方便讨论,咱们将初始化后的内存布局图简化以下:ui
Nginx slab自己没有对外提供专门的按页分配内存的接口,具体采用哪一种分配方式是在内部根据传入的size值并结合必定的算法来进行决策的。以ngx_slab_alloc为例:spa
void * ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size) { void *p; ngx_shmtx_lock(&pool->mutex); p = ngx_slab_alloc_locked(pool, size); ngx_shmtx_unlock(&pool->mutex); return p; } 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; /*若要求分配的内存大小超过1/2页,则采用“按页分配”的方式*/ /*注意,这里的判断在最新的版本里好像已经改成">"了,由于1/2页自己就是一个管理分级*/ if (size >= ngx_slab_max_size) { ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab alloc: %uz", size); /*将size向上取整到页大小的整数倍上*/ page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift) + ((size % ngx_pagesize) ? 1 : 0)); if (page) { p = (page - pool->pages) << ngx_pagesize_shift; p += (uintptr_t) pool->start; } else { p = 0; } goto done; } ... ... done: ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab alloc: %p", p); 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; /*“free”是空闲页管理单元链表头*/ for (page = pool->free.next; page != &pool->free; page = page->next) { /*当前连续空闲空间中的页数是否可以知足分配的需求*/ if (page->slab >= 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->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; } } ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory"); return NULL; }
下面这两幅图很是直观地说明了当初始化完成以后(假设此时共有N个空闲页),分别申请m(< N)页和N页内存时的情形。.net
下面再进一步来看一下当进行过若干次页分配后的内存空间布局,这里咱们假设分配的顺序为:m0页、1页、m1页、1页:debug
有了上面的几幅图,再结合ngx_slab_alloc_pages()的源码,就很容易理解“基于页的内存分配”流程和实现机制了。code