訪問這裏,獲取更多原創內容。數組
說明:本系列的文章基於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頁的分配時就容易多了。