本章我们介绍一下Nginx中基于slab的内存分配机制。
1. 相关宏定义
1) 所分配的内存大小尺寸
这里用2位
来表示所分配的内存大小尺寸,2位最多可以表示4种尺寸:
-
NGX_SLAB_PAGE_MASK: 表示尺寸的掩码
-
NGX_SLAB_PAGE: 超大内存,大于等于ngx_slab_max_size
-
NGX_SLAB_BIG: 大块内存,大于ngx_slab_exact_size而小于ngx_slab_max_size
-
NGX_SLAB_EXACT: 中等内存,等于ngx_slab_exact_size
-
NGX_SLAB_SMALL: 小块内存,小于ngx_slab_exact_size
下面是exact size与max size的计算:
ngx_slab_max_size = ngx_pagesize / 2;
ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));
2) 掩码设置
在当前我们使用的32bit Ubuntu环境中,NGX_PTR_SIZE
的值为4,因此:
-
NGX_SLAB_PAGE_FREE: 值为0, 用于指示当前page处于空闲状态
-
NGX_SLAB_PAGE_BUSY: 值为0xffffffff,用于指示当前page处于busy状态(针对大于ngx_slab_max_size的内存块)
-
NGX_SLAB_PAGE_START: 值为0x80000000,用于指示连续多个页面的开始页
-
NGX_SLAB_SHIFT_MASK: 值为0x0000000f, 当所分配的空间大于ngx_slab_exact_size且小于等于ngx_slab_max_size时,page->slab的高NGX_SLAB_MAP_MASK位用作位图,用于标识当前页
中内存块的分配情况,低NGX_SLAB_SHIFT_MASK位用于标识当前页的内存块大小移位
的掩码。比如当前内存块大小为256,即1<<8
,那么page->slab的低NGX_SLAB_SHIFT_MASK位的值就是8。
-
NGX_SLAB_MAP_MASK: 值为0xffff0000, 当所分配的空间大于ngx_slab_exact_size且小于等于ngx_slab_max_size时, page->slab的高NGX_SLAB_MAP_MASK位用作位图
-
NGX_SLAB_MAP_SHIFT: 值为16,当所分配的空间大于ngx_slab_exact_size且小于等于ngx_slab_max_size时, page->slab的高16位作为位图
-
NGX_SLAB_BUSY: 值为0xffffffff, 用于表示当前slab处于busy状态(针对小于等于ngx_slab_max_size的内存块)
3) 调试信息
我们当前并不支持NGX_DEBUG_MALLOC
与NGX_HAVE_DEBUG_MALLOC
,因此执行的是如下宏:
#define ngx_slab_junk(p, size)
2. 相关静态函数声明及变量定义
3. 函数ngx_slab_init()
本函数用于初始化整个ngx_slab_pool_t结构,下面我们简要分析一下函数的实现:
下面我们画出初始化之后的示意图:
4. 函数ngx_slab_alloc()
本函数首先使用pool->mutex
加锁,然后再调用ngx_slab_alloc_locked()函数分配指定大小的空间。
5. 函数ngx_slab_alloc_locked()
本函数用于分配指定大小的内存空间。这里slab将不等长的内存大小划分为4个大类:
-
小块内存(NGX_SLAB_SMALL): 内存大小 < ngx_slab_exact_size
-
中等内存(NGX_SLAB_EXACT): 内存大小 == ngx_slab_exact_size
-
大块内存(NGX_SLAB_BIG): ngx_slab_exact_size < 内存大小 <= ngx_slab_max_size
-
超大内存(NGX_SLAB_PAGE): ngx_slab_max_size < 内存大小
ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));
ngx_slab_exact_size表示uintptr_t
slab,此种情况下的slab用作bitmap,表示一页中的内存块使用状况。slab中的所有位(8 * sizeof(uintptr_t))刚好可以一一对应于一页找那个的大小为ngx_slab_exact_size的每一块内存。
下面我们详细分析一下函数的执行流程:
6. 函数ngx_slab_calloc()
与ngx_slab_alloc()函数类似,不过这里会将分配的内存清0。
7. 函数ngx_slab_calloc_locked()
与ngx_slab_alloc_locked()函数类似,不过这里会将分配的内存清0.
8. 函数ngx_slab_free()
本函数首先使用pool->mutex加锁,然后再调用ngx_slab_free_locked()函数来释放对应的内存。
9. 函数ngx_slab_free_locked()
本函数用于将对应的空间释放回内存池。下面我们简要分析一下函数的执行流程:
10. 函数ngx_slab_alloc_pages()
本函数用于从pool中分配出指定的 n 页。下面我们简单分析一下函数的执行流程:
11. 函数ngx_slab_free_pages()
此函数用于释放指定页数的page。下面我们简要分析一下函数的执行流程:
下面给出一张释放pages的示意图:
12. 函数ngx_slab_error()
用于打印slab内存分配相关的错误消息
[参看]
-
slab
-
Linux内存管理中的slab分配器
-
共享内存管理之slab机制
-
nginx slab内存管理
-
Nginx开发从入门到精通