代码版本:nginx 1.15.8
系统类型:64 位 linux
初始化共享内存管理结构
ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t));
创建共享内存
-
注册一块共享内存
ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag);
shm_zone = ngx_list_push(&cf->cycle->shared_memory); // 从共享内存管理链表中分配一个管理对象
一般在用到共享内存的模块中调用,如 ngx_http_limit_req_zone, ngx_http_lua_shared_memory_add
-
分配内存空间
解析完配置后,统一为注册的共享内存分配内存空间
part = &cycle->shared_memory.part;
shm_zone = part->elts;
for (i = 0; /* void */ ; i++) { // 遍历共享内存管理链表
if (i >= part->nelts) { // 链表的当前分片已遍历完
if (part->next == NULL) {
break;
}
part = part->next; // 开始遍历链表的下一个分片
shm_zone = part->elts;
i = 0;
}
shm_zone[i].shm.log = cycle->log;
ngx_shm_alloc(&shm_zone[i].shm); // 映射内存空间
ngx_init_zone_pool(cycle, &shm_zone[i]);
shm_zone[i].init(&shm_zone[i], NULL);
}
ngx_shm_alloc(&shm_zone[i].shm); // 映射一块指定大小的匿名、共享、可读写内存空间
shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
-
初始化每块内存的管理结构
ngx_init_zone_pool(cycle, &shm_zone[i]);
sp = (ngx_slab_pool_t *) zn->shm.addr; // 开始位置分配一个 slab 管理器
sp->end = zn->shm.addr + zn->shm.size; // 共享内存结束指针
sp->min_shift = 3; // 一次最小申请 8 字节空间
sp->addr = zn->shm.addr; // 共享内存起始指针
ngx_shmtx_create(&sp->mutex, &sp->lock, file); // 初始化共享内存互斥锁
ngx_slab_init(sp); // 初始化共享内存的 slab 管理器
注:slot 分级已申请内存的size - 1 的二进制位数减 min_shift 作为级别,如 size = 16,则二进制位数为4,slot = shift - pool->min_shift;,级别则为1
总共可分 8B, 16B, 32B, 64B, 128B, 256B, 512B, 1024B, 2048B 九级
void
ngx_slab_init(ngx_slab_pool_t *pool)
{
u_char *p;
size_t size;
ngx_int_t m;
ngx_uint_t i, n, pages;
ngx_slab_page_t *slots;
pool->min_size = 1 << pool->min_shift; // 最小分配chunk 大小 8 Byte
slots = (ngx_slab_page_t *) ((u_char *) (pool) + sizeof(ngx_slab_pool_t)); // 分级数组
p = (u_char *) slots;
size = pool->end - p; // 剩余可用空间的大小
ngx_slab_junk(p, size); // 初始化共享内存数据块
n = ngx_pagesize_shift - pool->min_shift; // 计算可分的级数,page_size为4kb时对应的shift为12,min_shift=3
// 则可分为 8,16,32,64,128,256,512,1024,2048 共九级
for (i = 0; i < n; i++) {
slots[i].slab = 0;
slots[i].next = &slots[i]; // next 初始化时指向自己,使用时用于判断是否分配过该级别内存
slots[i].prev = 0;
}
p += n * sizeof(ngx_slab_page_t); // 跳过 slot 分级数组区域
pool->stats = (ngx_slab_stat_t *) p; // 与 slot 分级对应的统计结构
ngx_memzero(pool->stats, n * sizeof(ngx_slab_stat_t));
p += n * sizeof(ngx_slab_stat_t); // 跳过统计结构
size -= n * (sizeof(ngx_slab_page_t) + sizeof(ngx_slab_stat_t));
pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t))); // 计算剩余空间还可分配出多少页
pool->pages = (ngx_slab_page_t *) p; // pool->pages 指向第一个页管理器
ngx_memzero(p, pages * sizeof(ngx_slab_page_t)); // 初始化每一页的管理器
page = pool->pages;
pool->free.slab = 0;
pool->free.prev = 0; // 双向链表把空闲页串联起来,链表节点可能是多个连续的空闲页
pool->free.next = page; // 指向可被分配的页管理器,初始指向第一片
page->slab = pages; // 当前页管理器后面多少个连续的空闲页
page->next = &pool->free;
page->prev = (uintptr_t) &pool->free;
// pool->start 指向实际存储数据的内存起始地址,并进行对齐
pool->start = (u_char *)ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t), ngx_pagesize);
m = pages - (pool->end - pool->start) / ngx_pagesize;
if (m > 0) { // 内存对齐后,可用内存可能变小,可划分的页数可能减少,重新计算页数
pages -= m;
pool->pages->slab = pages;
}
pool->last = pool->pages + pages;
pool->pfree = pages; // 空闲页数
pool->log_nomem = 1;
pool->log_ctx = &pool->zero;
pool->zero = '\0';
}
内存分配
-
大块内存分配
- 待分配空间大于 2K,直接按整页分配
- 每个页都有一个管理器,当前页空闲时,如果后面连续的页也是空闲的,则用第一个页管理器统一管理,slab 记录管理的空闲页数
- 如果一次分配了多个页,统一用第一个页管理器管理,第一个页管理器中slab 记录管理的页数和 NGX_SLAB_PAGE_START 标记,其余页的 slab 标记 NGX_SLAB_PAGE_BUSY,每个页的 prev 记录页类型 NGX_SLAB_PAGE
- 被整页分配的页不会挂载到任何链表上,所以 page->next 为 NULL,在回收时会用到
void * ngx_slab_alloc_locked(ngx_slab_pool