#內存管理的藝術# 之 Nginx slab的實現 --- 第二篇「基於頁的內存分配」

訪問這裏,獲取更多原創內容。    算法

    說明:本系列的文章基於Nginx-1.5.0版本代碼。函數

    在上一篇中已經介紹了Nginx slab分配器的基本原理和內存空間佈局,如今咱們將在此基礎上引入「基於頁的內存分配」的相關內容。之因此這樣安排是由於它的實現相對於「基於塊的內存分配」要簡單許多,同時它又是「基於塊的內存分配」的基礎,以它爲突破口怎麼看都是最好的選擇:)佈局

    在」基於頁的內存分配「流程中只須要用到」page頁內存管理單元「,而不涉及」分級內存管理單元「,爲了方便討論,咱們將初始化後的內存佈局圖簡化以下:ui


 

1、內存的分配

    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

相關文章
相關標籤/搜索