之前我们学习到Nginx的worker进程之间通过共享内存进行通信。
Nginx在解析完配置文件,把即将使用的共享内存全部以链表的形式组织在全局变量cf->cycle->shared_memory,再进行实际的内存分配。
我们也知道了共享内存上 可以使用 红黑树、链表这样的数据结构。
但这些数据结构都是由一个一个的节点组成的,每个节点都需要内存去存放。
那怎么把一整块的共享内存 分配成小块给这些节点使用的呢?
Nginx使用了Slab具体是怎么管理共享内存的?
其实Nginx 的slab机制与Linux的slab机制基本原理是一致的,当然Linux 的slab机制肯定要更加复杂。
Slab会把整个共享内存分为多个页。假设每个页为4K,会把这4K切分成多个slot,如:32byte的slot,64byte的slot,128byte的slot,这些slot是成倍的方式增长的。也就是时候一页里会有多个slot。
那么要存储34字节的内容也会占用64字节,可以看出这种存储是有内存空间浪费的,但是对内存性能的提升是显著的。 这种内存分配方式叫最佳适应算法(best fit )关于内存分配算法 还有 首次适应算法(first Fit)、循环首次适用算法(next fit )、最坏适应算法(worst fit)以后有机会我们一起学习操作系统的时候 再系统学习这块的知识。
整体结构如图
这种Bestfit 有如下特点:
- 适合小对象
- 避免碎片
- 避免重复初始化
我们一起来看下 slab 相关结构体 文件名:ngx_slab.h
typedef struct ngx_slab_page_s ngx_slab_page_t;
// 用于管理内存页。
struct ngx_slab_page_s {
uintptr_t slab;
ngx_slab_page_t *next;
uintptr_t prev;
};
//用于统计信息。
typedef struct {
ngx_uint_t total;
ngx_uint_t used;
ngx_uint_t reqs;
ngx_uint_t fails;
} ngx_slab_stat_t;
typedef struct {
ngx_shmtx_sh_t lock;
size_t min_size; // 可分配的最小内存
size_t min_shift; // 最小内存对应的偏移值 (如果是3 则代表 min_size是 2^3)
ngx_slab_page_t *pages; // 指向第一页的管理结构
ngx_slab_page_t *last; // 指向最后一页的管理结构
ngx_slab_page_t free; // 用于管理空闲页面
ngx_slab_stat_t *stats; // 每种规格内存的统计信息
ngx_uint_t pfree; // 空闲页数
void *data;
void *addr;
} ngx_slab_pool_t;
还提供了一组API来操作 如:
// 共享内存初始化,pool指向某个共享内存的首地址。
void ngx_slab_init(ngx_slab_pool_t *pool);
// 从pool 指向的共享内存快中申请大小 是size的内存。
void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size);
// 释放pool 分配的某个内存。
void ngx_slab_free(ngx_slab_pool_t *pool, void *p);
ngx_slab_pool_t: 是共享内存管理的主要就靠这家伙了,处于共享内存的头部,我们可以从这个结构体里分配或释放共享内存里的内存。
ngx_slab_page_t: 管理内存页,用在next字段。有9种大小规格:8Byte,16Byte,32Byte,64Byte,128Byte,256Byte,512Byte,1024Byte,2048Byte
ngx_slab_stat_t: 用于统计每种规格page的信息。所以也有9种。
整体结构如下图:
我们可以通过slab_stat模块统计到每个slot共分配数,使用数,请求数,失败数。这就方便我们查看共享内存的使用情况。
本文暂时没有评论,来添加一个吧(●'◡'●)