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