nginx的slab分配器主要和共享内存(nginx自己实现的共享内存 采用mmap或者shm实现)一起使用,Nginx在解析完配置文件,把即将使用的共享内存全部以list链表的形式,对共享内存进行管理和划分。在nginx_cycle.c中
static ngx_int_t
ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn)
{
u_char *file;
ngx_slab_pool_t *sp;
sp = (ngx_slab_pool_t *) zn->shm.addr;
if (zn->shm.exists) {
if (sp == sp->addr) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"shared zone \"%V\" has no equal addresses: %p vs %p",
&zn->shm.name, sp->addr, sp);
return NGX_ERROR;
}
sp->end = zn->shm.addr + zn->shm.size;
sp->min_shift = 3;
sp->addr = zn->shm.addr;
.......
ngx_slab_init(sp);//在共享内存建立完成之后开始初始化 slab分配器
可以看到共享内存的开始部分用来存储ngx_slab_pool_t。
slab对于内存的管理主要有两个方面:一个是page的管理(包括静态对齐和动态分配),另一个是对小于page的slot 小内存的管理,slot内存段的管理是琐碎而频繁。
slab中常变量的表述如下:
变量名 值 描述
ngx_pagesize 4096 系统页大小
ngx_pagesize_shift 12 对应上项 4096=1<<12
pool->min_shirt 3 固定值
pool->min_size 8 最小分配8个字节
内存对齐能够减少不必要的碎片管理,另外内存对齐对内存访问的性能也有影响。参见这里.
整个slab管理器的定义如下
typedef struct ngx_slab_page_s ngx_slab_page_t;
struct ngx_slab_page_s {
uintptr_t slab; //32位表示page内128个字节的使用
ngx_slab_page_t *next; 下一个page
uintptr_t prev;
};
typedef struct {
ngx_shmtx_sh_t lock;
size_t min_size;
size_t min_shift;
ngx_slab_page_t *pages;
ngx_slab_page_t free;
u_char *start;
u_char *end;
ngx_shmtx_t mutex;
u_char *log_ctx;
u_char zero;
unsigned log_nomem:1;
void *data;
void *addr;
} ngx_slab_pool_t;
第一个问题:如何根据管理结构page获得对应的内存页的起始地址P呢?计算方法如下
p = (page - pool->pages) << ngx_pagesize_shift;
p += (uintptr_t) pool->start;
第二个问题,页面对齐可以提高内存的访问速度,slab是如何做到页面对齐的呢? 代码如下 实际上浪费了最后一个不满的page,在ngx_slab.c中
m = pages - (pool->end - pool->start) / ngx_pagesize;
if (m > 0) {
pages -= m;
pool->pages->slab = pages;
}
动态页面的管理相对简单,既然所有闲置page都在链表上直接移除(分配时)就可以, 回收page的时候在头插到链表中,这里记住是头插。
业内slot的管理相对琐碎,不过slab的威力就在对琐碎频繁的小内存管理的能力。业内slot的管理,首先slab把slot划分为8,16,32,64,128,256,512,1024,2048,9条链表在ngx_slab_init()中:
slots = (ngx_slab_page_t *) p; //取得页首地址,实际上就是存放的page的管理struct
n = ngx_pagesize_shift - pool->min_shift; //12-3=9
for (i = 0; i < n; i++) {
slots[i].slab = 0;
slots[i].next = &slots[i];
slots[i].prev = 0;
}
忘了说了,但超过2048个字节的申请,直接转向page的申请和管理不再由slot负责。
参考资料:
《深入剖析Nginx》高凯群