Sword nginx slab源码解析五(申请空间释放)

oid ngx_slab_free(ngx_slab_pool_t *pool, void *p)
{
    ngx_shmtx_lock(&pool->mutex);

    ngx_slab_free_locked(pool, p);

    ngx_shmtx_unlock(&pool->mutex);
}


void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)
{
    size_t            size;
    uintptr_t         slab, m, *bitmap;
    ngx_uint_t        i, n, type, slot, shift, map;
    ngx_slab_page_t  *slots, *page;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p);

    if ((u_char *) p < pool->start || (u_char *) p > pool->end) 
    {
        // 释放的指针越界
        ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool");
        goto fail;
    }

    n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;
    // 找到管理该页的 pages[n]
    page = &pool->pages[n];

    slab = page->slab;

    // 获取当前页的类型
    type = ngx_slab_page_type(page);

    switch (type) {

    case NGX_SLAB_SMALL:

        /*
        设计说明:
            当页的类型是 NGX_SLAB_SMALL,表示该页上都是小于128字节的slot
            page->slab 存储的是 slot的偏移量
        */
        shift = slab & NGX_SLAB_SHIFT_MASK;

        // 获取slot大小
        size = (size_t) 1 << shift;

        // 因为地址对齐,p是slot的起始地址,因此p的地址一定是(size-1)的倍数,例如size = 8,那么p的地址一定是8的倍数
        if ((uintptr_t) p & (size - 1)) 
        {
            goto wrong_chunk;
        }

        // 定位p处于那个slot中
        n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift;

        // 获取p在当前bitmap[n]中的偏移量
        m = (uintptr_t) 1 << (n % (8 * sizeof(uintptr_t)));

        // 获取p在那个bitmap中
        n /= 8 * sizeof(uintptr_t);

        // 获取bitmap
        bitmap = (uintptr_t *)
                             ((uintptr_t) p & ~((uintptr_t) ngx_pagesize - 1));

        if (bitmap[n] & m) 
        {
            // 获取slot下标
            slot = shift - pool->min_shift;

            if (page->next == NULL) 
            {
                slots = ngx_slab_slots(pool);

                page->next = slots[slot].next;
                slots[slot].next = page;

                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;
                page->next->prev = (uintptr_t) page | NGX_SLAB_SMALL;
            }

            // 置零,表示该slot重新可用
            bitmap[n] &= ~m;

            /*
            设计说明:
                假设shift = 3
                ngx_pagesize >> shift 表示每页有512个slot
                每个slot需要1bit来表示
                1 << shift 每个slot大小(单位字节)
                8bit 等于 1字节
                n 即表示512个slot需要占用多少个slot

            */
            n = (ngx_pagesize >> shift) / ((1 << shift) * 8);

            if (n == 0) {
                n = 1;
            }

            /*
            设计说明:
                每页之前有一部分slot是用来存储bitmap信息的,这部分肯定需要跳过去,因为他们肯定永久被占用
                假设shift = 3
                n = 8,一个bitmap可以记录32个slot信息,因此bitmap[0]就可以表示记录slot信息的slot块
                i = 0
                表示bitmap[0]就可以表示记录slot信息的slot块
            */
            i = n / (8 * sizeof(uintptr_t));

            m = ((uintptr_t) 1 << (n % (8 * sizeof(uintptr_t)))) - 1;

            if (bitmap[i] & ~m) 
            {
                // 记录slot位信息的bitmap上都存在为1的bit位,无法释放该页
                goto done;
            }

            // 遍历其他的bitmap 看看是否还有userd
            map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));

            for (i = i + 1; i < map; i++) {
                if (bitmap[i]) 
                {
                    // 只要存在userd,不释放该页
                    goto done;
                }
            }

            // 释放该页

            ngx_slab_free_pages(pool, page, 1);

            pool->stats[slot].total -= (ngx_pagesize >> shift) - n;

            goto done;
        }

        goto chunk_already_free;

    case NGX_SLAB_EXACT:

        /*
        设计说明:
            ((uintptr_t) p & (ngx_pagesize - 1)) 计算出p位于页中的相对位置
            (((uintptr_t) p & (ngx_pagesize - 1)) >> ngx_slab_exact_shift) 根据ngx_slab_exact_shift计算出p的偏移量
            m 即p位于slots[slot]相对位置
        */
        m = (uintptr_t) 1 <<
                (((uintptr_t) p & (ngx_pagesize - 1)) >> ngx_slab_exact_shift);

        size = ngx_slab_exact_size;

        if ((uintptr_t) p & (size - 1)) 
        {
            // 因为内存对齐,因此(uintptr_t) p & (size - 1)一定为0
            goto wrong_chunk;
        }

        /*
        设计说明:
            NGX_SLAB_EXACT中,slab存储着slot块的使用状况
        */

        if (slab & m) 
        {
            
            slot = ngx_slab_exact_shift - pool->min_shift;

            if (slab == NGX_SLAB_BUSY) 
            {
                // page 从满变成不满状态

                slots = ngx_slab_slots(pool);

                // 重新将该页加入到空闲列表中
                page->next = slots[slot].next;
                slots[slot].next = page;

                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;
                page->next->prev = (uintptr_t) page | NGX_SLAB_EXACT;
            }

            // 更新记录位
            page->slab &= ~m;

            if (page->slab) 
            {
                goto done;
            }

            // 当前页已经全部空闲
            ngx_slab_free_pages(pool, page, 1);

            pool->stats[slot].total -= 8 * sizeof(uintptr_t);

            goto done;
        }

        goto chunk_already_free;

    case NGX_SLAB_BIG:

        // slab的高16位是slot块的位图,低16位用于存储slot块大小的偏移

        // 获取shift
        shift = slab & NGX_SLAB_SHIFT_MASK;
        // 获取slot大小
        size = (size_t) 1 << shift;

        if ((uintptr_t) p & (size - 1)) 
        {
            goto wrong_chunk;
        }

        /*
        设计说明:
            ((uintptr_t) p & (ngx_pagesize - 1)) >> shift  获取当前p相对偏移位置
            因为slab只有高16位用来存储slot信息, 1 << (((uintptr_t) p & (ngx_pagesize - 1)) >> shift)实际上并不正确
            1 << (((uintptr_t) p & (ngx_pagesize - 1)) >> shift) 只是描述低16位的情况,因此需要加16(NGX_SLAB_MAP_SHIFT),才是高16位的情况
        */
        m = (uintptr_t) 1 << ((((uintptr_t) p & (ngx_pagesize - 1)) >> shift)
                              + NGX_SLAB_MAP_SHIFT);

        if (slab & m) 
        {
            slot = shift - pool->min_shift;

            if (page->next == NULL) 
            {
                slots = ngx_slab_slots(pool);

                page->next = slots[slot].next;
                slots[slot].next = page;

                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;
                page->next->prev = (uintptr_t) page | NGX_SLAB_BIG;
            }

            page->slab &= ~m;

            if (page->slab & NGX_SLAB_MAP_MASK) {
                goto done;
            }

            ngx_slab_free_pages(pool, page, 1);

            pool->stats[slot].total -= ngx_pagesize >> shift;

            goto done;
        }

        goto chunk_already_free;

    case NGX_SLAB_PAGE:

        // 整页

        if ((uintptr_t) p & (ngx_pagesize - 1)) 
        {
            goto wrong_chunk;
        }

        if (!(slab & NGX_SLAB_PAGE_START)) 
        {
            ngx_slab_error(pool, NGX_LOG_ALERT,
                           "ngx_slab_free(): page is already free");
            goto fail;
        }

        if (slab == NGX_SLAB_PAGE_BUSY) 
        {
            // 这只是一个分页,释放的时候必须以申请页数释放
            ngx_slab_error(pool, NGX_LOG_ALERT,
                           "ngx_slab_free(): pointer to wrong page");
            goto fail;
        }

        // 获取申请页大小
        size = slab & ~NGX_SLAB_PAGE_START;

        ngx_slab_free_pages(pool, page, size);

        ngx_slab_junk(p, size << ngx_pagesize_shift);

        return;
    }

    /* not reached */

    return;

done:

    pool->stats[slot].used--;

    ngx_slab_junk(p, size);

    return;

wrong_chunk:

    ngx_slab_error(pool, NGX_LOG_ALERT,
                   "ngx_slab_free(): pointer to wrong chunk");

    goto fail;

chunk_already_free:

    ngx_slab_error(pool, NGX_LOG_ALERT,
                   "ngx_slab_free(): chunk is already free");

fail:

    return;
}
static void ngx_slab_free_pages(ngx_slab_pool_t* pool, ngx_slab_page_t* page, ngx_uint_t pages)
{
    ngx_slab_page_t  *prev, *join;

    pool->pfree += pages;

    // 更新slab=pages
    page->slab = pages--;

    if (pages) 
    {
        // 本次释放的页数大于1,对后面的页进行清理
        ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t));
    }

    if (page->next) 
    {
        prev = ngx_slab_page_prev(page);
        prev->next = page->next;
        page->next->prev = page->prev;
    }

    join = page + page->slab;

    if (join < pool->last) 
    {
        // join 不是最后一个 page

        if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) 
        {
            // 表示释放的page是整页类型

            if (join->next != NULL) 
            {
                pages += join->slab;
                page->slab += join->slab;

                prev = ngx_slab_page_prev(join);
                prev->next = join->next;
                join->next->prev = join->prev;

                join->slab = NGX_SLAB_PAGE_FREE;
                join->next = NULL;
                join->prev = NGX_SLAB_PAGE;
            }
        }
    }

    if (page > pool->pages) 
    {
        join = page - 1;

        if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) 
        {

            if (join->slab == NGX_SLAB_PAGE_FREE) 
            {
                join = ngx_slab_page_prev(join);
            }

            if (join->next != NULL) {
                pages += join->slab;
                join->slab += page->slab;

                prev = ngx_slab_page_prev(join);
                prev->next = join->next;
                join->next->prev = join->prev;

                page->slab = NGX_SLAB_PAGE_FREE;
                page->next = NULL;
                page->prev = NGX_SLAB_PAGE;

                page = join;
            }
        }
    }

    if (pages) {
        page[pages].prev = (uintptr_t) page;
    }

    page->prev = (uintptr_t) &pool->free;
    // 将原先的空闲页挂到page上
    page->next = pool->free.next;

    page->next->prev = (uintptr_t) page;

    pool->free.next = page;
}

 

posted on 2022-07-23 12:39  寒魔影  阅读(75)  评论(0编辑  收藏  举报

导航