Nginx slab 用于程序間的共享記憶體
轉自 https://my.oschina.net/u/2310891/blog/672539
說明:本系列的文章基于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分級管理單元*/
同時還聲明了對外提供的幾個函數原型,分别是:
用于初始化
void ngx_slab_init(ngx_slab_pool_t *pool);
用于記憶體配置設定的:
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的差別在于是否在配置設定的同時将記憶體清零*/
用于記憶體釋放的:
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配置設定器的基本結構是怎麼樣的。
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 == ) {
/*slab配置設定器最大配置設定大小(slab配置設定器用于配置設定小于一頁的記憶體申請,由于實際會以2的幂次方為基準向上取整,是以超過1/2頁大小的記憶體申請也被取整為1頁)*/
ngx_slab_max_size = ngx_pagesize / ;
/*當使用bit位來标記塊的使用情況時,如果想用一個uintptr_t類型的數來标記一整頁中的所有塊,則需要将一頁分為(8 * sizeof(uintptr_t)個塊,每塊大小為ngx_slab_exact_size*/
ngx_slab_exact_size = ngx_pagesize / ( * sizeof(uintptr_t));
/*根據ngx_slab_exact_size計算對應的塊大小移位ngx_slab_exact_shift*/
for (n = ngx_slab_exact_size; n >>= ; ngx_slab_exact_shift++) {
/* void */
}
}
/**/
/*slab的最小配置設定單元,當申請的記憶體比min_size還小時,則取整到min_size;
通常min_shift=3,則min_size=8*/
pool->min_size = << 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 = ; i < n; i++) {
slots[i].slab = ;
slots[i].next = &slots[i];/*表明分級數組中還沒有要管理的頁*/
slots[i].prev = ;
}
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 = ;
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 > ) {
pages -= m;
pool->pages->slab = pages;
}
pool->log_ctx = &pool->zero;
pool->zero = '\0';
}
初始化完成之後,整個記憶體結構布局就是這個樣子滴:
圖中的各個标記基本保持與ngx_slab_init()函數中一緻, 其中,N = pages - m,即經過對齊調整後的實際記憶體頁數。
有了這個圖做鋪墊,我們再讨論page頁的配置設定時就容易多了。