• Sword nginx slab源码解析四(slot块分配)


    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, m, mask, *bitmap;
        ngx_uint_t        i, n, slot, shift, map;
        ngx_slab_page_t*  page, * prev, * slots;
    
        if (size > ngx_slab_max_size) 
        {
            // 如果申请的共享内存大于2048字节
    
            /*
            设计说明:
                因为申请的字节大于2048字节,因此直接分配一页(4096字节)
            */
    
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
                           "slab alloc: %uz", size);
    
            // 获取空闲页
            page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift)
                                              + ((size % ngx_pagesize) ? 1 : 0));
            if (page) 
            {
                p = ngx_slab_page_addr(pool, page);
    
            } else 
            {
                p = 0;
            }
    
            goto done;
        }
    
        if (size > pool->min_size) 
        {
            // 申请的字节大于pool->min_size(8字节)
            shift = 1;
            // 计算出偏移量
            for (s = size - 1; s >>= 1; shift++) { /* void */ }
            // 获取slot下标
            slot = shift - pool->min_shift;
    
        } 
        else 
        {
            // 如果申请的字节数小于8字节,按8字节算
            shift = pool->min_shift;
            // slots下标为0
            slot = 0;
        }
    
        pool->stats[slot].reqs++;
    
        ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
                       "slab alloc: %uz slot: %ui", size, slot);
    
        // 
        slots = ngx_slab_slots(pool);
        page = slots[slot].next;
    
        if (page->next != page) 
        {
            // 已经初始化过slots
    
            if (shift < ngx_slab_exact_shift) 
            {
                // size 小于 128字节
    
                // 提取当前页的 bitmap
                bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);
    
                // 计算bitmap数组的大小
                /*
                设计说明:
                    假设ngx_pagesize=4096字节,shift = 3
                    那么一页可以拆分512个8字节的slot块,即需要512bit来标记每个slot的使用情况(已使用为1,未使用为0)
                    512bit即64字节,每个bitmap单元需要4个字节,因此需要16个bitmap单元才能表示512个slot
                    map = 16
                */
                map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));
    
                // 遍历bitmap数组
                for (n = 0; n < map; n++) 
                {
    
                    if (bitmap[n] != NGX_SLAB_BUSY) 
                    {
                        // 当前bitmap[n]还有可用的slot
                        for (m = 1, i = 0; m; m <<= 1, i++) 
                        {
                            if (bitmap[n] & m) 
                            {
                                // 当前bit对应的slot块已经被使用,跳过
                                continue;
                            }
    
                            // 找到一个未使用的bit
    
                            // 设置当前bit为1,表示被占用
                            bitmap[n] |= m;
    
                            // 计算偏移量
                            /*
                            设计说明:
                                n表示bitmap的下标,每个bitmap可以表示 8 * sizeof(uintptr_t) 个slot
                                i 为当前 bitmap[n] 的偏移量
                                (n * 8 * sizeof(uintptr_t) + i) 表示使用到当前页的第几个slot
                                (n * 8 * sizeof(uintptr_t) + i) << shift 可以计算出相对于 bitmap 的偏移量
                            */
                            i = (n * 8 * sizeof(uintptr_t) + i) << shift;
    
                            // 定位到当前slot
                            /*
                            设计说明:
                                i 是偏移的字节数
                                bitmap 是当前页起始位置
                                (uintptr_t) bitmap 将指针类型转成整数类型,方便进行偏移量计算
                                或者 p = (char*)bitmap + i 也是可以的
                            */
                            p = (uintptr_t) bitmap + i;
    
                            // 记录使用
                            pool->stats[slot].used++;
    
                            if (bitmap[n] == NGX_SLAB_BUSY) 
                            {
                                // 当前bitmap[n]管理的slot已经完全被使用完了
    
                                for (n = n + 1; n < map; n++) 
                                {
                                    // 当前页还有可以分配的slot
                                    if (bitmap[n] != NGX_SLAB_BUSY) 
                                    {
                                        goto done;
                                    }
                                }
    
                                // 当前页上所有的slot已经被使用光了
    
                                // 从链表上将该页摘除
                                prev = ngx_slab_page_prev(page);
                                prev->next = page->next;
                                page->next->prev = page->prev;
    
                                // 标记该页属性
                                page->next = NULL;
                                page->prev = NGX_SLAB_SMALL;
                            }
    
                            goto done;
                        }
                    }
                }
    
            } 
            else if (shift == ngx_slab_exact_shift) 
            {
                // 申请slot为128字节
    
                for (m = 1, i = 0; m; m <<= 1, i++) 
                {
                    if (page->slab & m) {
                        continue;
                    }
    
                    page->slab |= m;
    
                    if (page->slab == NGX_SLAB_BUSY) 
                    {
                        // 当前页已经用光了
                        prev = ngx_slab_page_prev(page);
                        prev->next = page->next;
                        page->next->prev = page->prev;
    
                        page->next = NULL;
                        page->prev = NGX_SLAB_EXACT;
                    }
    
                    p = ngx_slab_page_addr(pool, page) + (i << shift);
    
                    pool->stats[slot].used++;
    
                    goto done;
                }
    
            } 
            else 
            {
                // 申请内存块大于128字节
    
                /*
                设计说明:
                    shift = 8(256字节),mask=0xFFFF0000
                    shift = 9(512字节),mask=0xFF0000
                    shift = 10(1024字节),mask=0xF0000
                */
                mask = ((uintptr_t) 1 << (ngx_pagesize >> shift)) - 1;
                mask <<= NGX_SLAB_MAP_SHIFT;
    
                for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;
                     m & mask;
                     m <<= 1, i++)
                {
                    // 查找可用的slot
                    if (page->slab & m) 
                    {
                        continue;
                    }
    
                    page->slab |= m;
    
                    if ((page->slab & NGX_SLAB_MAP_MASK) == mask) 
                    {
                        // 表示当前页上slot已经全部用完
                        prev = ngx_slab_page_prev(page);
                        prev->next = page->next;
                        page->next->prev = page->prev;
    
                        page->next = NULL;
                        page->prev = NGX_SLAB_BIG;
                    }
    
                    p = ngx_slab_page_addr(pool, page) + (i << shift);
    
                    pool->stats[slot].used++;
    
                    goto done;
                }
            }
    
            ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_alloc(): page is busy");
            ngx_debug_point();
        }
    
        // 申请一页
        page = ngx_slab_alloc_pages(pool, 1);
    
        if (page) 
        {
    
            if (shift < ngx_slab_exact_shift) 
            {
                // 申请内存块小于128字节
    
                // 页的开头内存作为bitmap
                /*
                设计说明:
                    每页的前面的n个slot不能用来分配给用户使用,他们是用来记录当前页上slot的使用情况的
                    每页上slot单元越多,需要记录slot的bitmap单元也就越多
                    目前最小的slot是8字节,一页可以分配512个slot,但是为了记录512个slot,那么需要512bit来记录,对应的字节是64字节
                    那么需要消耗8个slot单元来记录当前页上slot信息
                    每个bitmap单元是4个字节,即需要16个bitmap单元来记录slots信息
                */
                bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);
                // 计算一页中可以划分多少slot
                /*
                设计说明:
                    ngx_pagesize >> shift 计算每页可以划分多少个slot
                    每个slot的大小是 1 << shift 
                    8bit 等于 1字节
                    该页上slot信息需要n个slot单元才能记录
                */
                n = (ngx_pagesize >> shift) / ((1 << shift) * 8);
    
                if (n == 0) 
                {
                    // 至少使用1个slot
                    n = 1;
                }
    
                /* "n" elements for bitmap, plus one requested */
                /*
                设计说明:
                    n + 1 表示需要使用n+1个slot单元来记录slot信息
                        为啥是n+1而不是n呢?
                        因为本次分配就要占据一个slot,所以是n+1
    
                    (8 * sizeof(uintptr_t) 表示每个 bitmap 可以记录32个slot信息
                    (n + 1) / (8 * sizeof(uintptr_t)) 表示记录slot需要占据bitmap单元的个数
                    即使是slot=8,那么当前页以记录信息为目的消耗的slot数目是8,那么一个bitmap也足够了
                */
                for (i = 0; i < (n + 1) / (8 * sizeof(uintptr_t)); i++) 
                {
                    bitmap[i] = NGX_SLAB_BUSY;
                }
                /*
                设计说明:
                    这里m的值就是为了将bitmap[i]前面 (n+1) bit全部设置为1
                    这是一种常用的位移操作,当物理公式记住就行,不需要理解
                */
                m = ((uintptr_t) 1 << ((n + 1) % (8 * sizeof(uintptr_t)))) - 1;
                // 更新bitmap单元
                bitmap[i] = m;
    
                // 计算bitmap单元的个数
                map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));
    
                for (i = i + 1; i < map; i++) 
                {
                    // 初始化为0
                    bitmap[i] = 0;
                }
    
                // 设置当前页的偏移量
                page->slab = shift;
                page->next = &slots[slot];
                // 标记当前页是用于分配小于128字节的slot
                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;
    
                slots[slot].next = page;
    
                // 新页可以为pool->stats[slot].total增加多少可用slot单元
                pool->stats[slot].total += (ngx_pagesize >> shift) - n;
    
                // 获取可用的内存节点
                p = ngx_slab_page_addr(pool, page) + (n << shift);
    
                // 更新userd
                pool->stats[slot].used++;
    
                goto done;
    
            } 
            else if (shift == ngx_slab_exact_shift) 
            {
                // 申请内存块等于128字节
    
                /*
                设计说明:
                    当申请内存块大小等于128字节的时候,使用page->slab来代替page
                    因为一页4096字节,至多可以划分出32个128字节大小的slot块
                    那么可以使用page->slab来记录slot的使用情况
                    对内存的使用很精细
                */
                // 标记第一块slot已经被占用
                page->slab = 1;
                page->next = &slots[slot];
                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;
    
                slots[slot].next = page;
    
                // 设置总数量增加32个slot
                pool->stats[slot].total += 8 * sizeof(uintptr_t);
    
                p = ngx_slab_page_addr(pool, page);
    
                // 已使用数量自增
                pool->stats[slot].used++;
    
                goto done;
    
            } 
            else 
            {
                // 申请内存块大于128字节
    
                /*
                设计说明:
                    当申请内存块大小大于128字节的时候,使用page->slab来代替page
                    因为一页4096字节,至多可以划分出16个大于128字节大小的slot块
                    那么可以使用page->slab的[高16位]来记录slot的使用情况
                    ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT)  高16位记录使用情况
                    低16位用来存储shift信息
    
                    备注:
                        32位机器上,NGX_SLAB_MAP_SHIFT=16,uintptr_t即unsigned int
                */
    
                page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift;
                page->next = &slots[slot];
                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;
    
                slots[slot].next = page;
    
                pool->stats[slot].total += ngx_pagesize >> shift;
    
                p = ngx_slab_page_addr(pool, page);
    
                pool->stats[slot].used++;
    
                goto done;
            }
        }
    
        p = 0;
    
        pool->stats[slot].fails++;
    
    done:
    
        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
                       "slab alloc: %p", (void *) p);
    
        return (void *) p;
    }
    
    
    void* ngx_slab_calloc(ngx_slab_pool_t* pool, size_t size)
    {
        void  *p;
    
        ngx_shmtx_lock(&pool->mutex);
    
        p = ngx_slab_calloc_locked(pool, size);
    
        ngx_shmtx_unlock(&pool->mutex);
    
        return p;
    }
    
    
    void* ngx_slab_calloc_locked(ngx_slab_pool_t* pool, size_t size)
    {
        void  *p;
    
        p = ngx_slab_alloc_locked(pool, size);
        if (p) {
            ngx_memzero(p, size);
        }
    
        return p;
    }

  • 相关阅读:
    Hibernate动态更新
    Spring MVC实现文件上传
    windows 常用命名
    有用的SQL查询
    SQL语句、EF DataAnnotation和EF Fluent API方式创建联合主键
    EF6学习笔记三十二:性能优化——实体缓存和翻译缓存
    EF6学习笔记三十一:性能优化(二)
    EF6学习笔记三十:性能优化——预编译视图
    EF6学习笔记二十九:并发冲突(三)
    EF6学习笔记二十八:并发冲突(二)
  • 原文地址:https://www.cnblogs.com/zhanggaofeng/p/16511759.html
Copyright © 2020-2023  润新知