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; }