天天看點

Nginx slab的實作 --- 第一篇“基本布局”

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配置設定器的基本結構是怎麼樣的。

Nginx 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';
}
           
初始化完成之後,整個記憶體結構布局就是這個樣子滴:
           
Nginx slab的實作 --- 第一篇“基本布局”

圖中的各個标記基本保持與ngx_slab_init()函數中一緻, 其中,N = pages - m,即經過對齊調整後的實際記憶體頁數。

有了這個圖做鋪墊,我們再讨論page頁的配置設定時就容易多了。
           

繼續閱讀