#內存管理的藝術# 之 Nginx slab的實現 --- 第一篇「基本佈局」

訪問這裏,獲取更多原創內容。數組

    說明:本系列的文章基於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頁的分配時就容易多了。

相關文章
相關標籤/搜索