在nginx的进程模型下,相似流量统计、流量控制、数据共享、等须要多个工做进程共同配合完成任务,共享内存是一个重要的进程通信的方案。本文介绍在nginx的代码中与共享内存相关的功能,包括ngx_shmem与ngx_slab的使用与注意事项,但不包括ngx_slab中实现的内存管理算法。html
ngx_shmem.c/h文件只是对mmap()/munmap()系统调用或者shmget()/shmdt()的一个很简单的封装。实现了ngx风格的基础库,能够申请和释放一段连续的共享内存空间。通常用于固定长度的共享数据使用,使用过程当中数据长度固定不会伸缩。nginx
typedef struct { u_char *addr; size_t size; ... } ngx_shm_t; ngx_int_t ngx_shm_alloc(ngx_shm_t *shm); void ngx_shm_free(ngx_shm_t *shm);
在ngxin中共享内存的使用流程,通常是由master进程建立,worker进程经过继承的方式得到内存指针。算法
关于ngx_shmem的使用,能够参考ngx_event_module_init()中部分片断,这部分代码在共享内存中建立了若干个变量,用于记录各个状态(accepted/reading/writing...)的请求数量,并在ngx_event_module中的几个关键事件入口对这几个变量进行加减统计操做。实现统计全部worker进程当前的请求状态。数据结构
shm.size = size; ngx_str_set(&shm.name, "nginx_shared_zone"); shm.log = cycle->log; if (ngx_shm_alloc(&shm) != NGX_OK) { return NGX_ERROR; } shared = shm.addr; ... ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl); ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl); ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl); ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl); ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl); ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl); ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
关于这个功能的更多细节,能够查看代码中的NGX_STAT_STUB宏定义相关代码与ngx_http_stub_status_module。并发
ngx_shmem是一层极简的封装,实现了共享内存的基本功能。但咱们程序中大部分的场景共享数据并不会一个固定大小的结构,而更可能是像ngx_array、ngx_list、ngx_queue、ngx_rbtree这类大小能够变化的数据结构。框架
咱们指望能有像ngx_pool_t同样能够动态申请释放空间一个内存池。ngx_slab正是一个这样的结构体,原理上与系统的malloc()有相识之处都是经过一系列算法实现对一段段内存片断的申请与释放。只不过ngx_slab操做的对象是基于ngx_shmem的共享内存。函数
先看一下ngx_slab的接口ui
typedef struct { ngx_shmtx_t mutex; ... void *data; /* 通常存放从pool中申请得到的根数据地址(pool中第一个申请的数据接口) */ void *addr; /* 使用ngx_shmem申请得到的共享内存基地址 */ } ngx_slab_pool_t; void ngx_slab_init(ngx_slab_pool_t *pool); void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size); void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size); void *ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size); void *ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size); void ngx_slab_free(ngx_slab_pool_t *pool, void *p); void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);
能够看到接口并不复杂,alloc与calloc的区别在因而否对申请得到的内存段清零,_locked结尾的接口表示操做的pool已是获取到锁的。在ngx_slab_pool_t的结构体有一个ngx_shmtx_t的互斥锁用于同步多进程同时访问pool的并发场景。注意ngx_slab_alloc()会先获取锁、而后申请空间、最后释放锁。而ngx_slab_alloc_locked()则直接申请空间,认为程序已经在其余逻辑中得到锁了。atom
在nginx的开发中使用ngx_shmem通常须要遵循如下初始化流程:指针
在这个流程中,涉及到ngx_shared_memory_add()接口与对应的ngx_shm_zone_t结构体。
struct ngx_shm_zone_s { void *data; ngx_shm_t shm; ngx_shm_zone_init_pt init; void *tag; void *sync; ngx_uint_t noreuse; /* unsigned noreuse:1; */ }; ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag);
其中值得一提的是noreuse属性,这个属性控制了在nginx的reload过程当中是否会从新申请共享内存。
因为关于ngx_init_cycle()函数较长,这个流程能够经过查找/* create shared memory */这个注释或者cycle->shared_memory这个对象查看相关代码。
关于ngx_slab更多细节的使用,建议能够参考ngx_http_limit_conn_module,这是经过共享内存实现链接数限制的模块,模块复杂度底,是一个很好的参考范例。
同时安利一波《深刻理解Nginx》做者 陶辉 在极客时间出版的《Nginx核心知识100讲》,近期618彷佛有打折活动,经过我分享的连接进行购买,我也将得到部分返现,感谢支持。