訪問這裏,獲取更多原創內容。 算法
說明:本系列的文章基於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