• 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;
    }
  • 相关阅读:
    软件安装
    ARIMA
    解决数据分析中的小知识点及问题
    Django详解之路由系统、中间件
    hdoj 1018
    程序员编程技术迅速提高终极攻略 (转自csdn)
    chapter 5:一个简单的规律问题。
    chapter 4:贪心
    7种qsort排序方法
    chapter 2:hdoj 1031(结构体的使用)
  • 原文地址:https://www.cnblogs.com/zhanggaofeng/p/16511763.html
Copyright © 2020-2023  润新知