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》高凯群
Nginx系列二 slab分配器