在nginx的进程模型下,类似流量统计、流量控制、数据共享、等需要多个工作进程共同配合完成任务,共享内存是一个重要的进程通讯的方案。本文介绍在nginx的代码中与共享内存相关的功能,包括ngx_shmem与ngx_slab的使用与注意事项,但不包括ngx_slab中实现的内存管理算法。
ngx_shmem的使用
ngx_shmem.c/h文件只是对mmap()/munmap()系统调用或者shmget()/shmdt()的一个很简单的封装。实现了ngx风格的基础库,可以申请和释放一段连续的共享内存空间。一般用于固定长度的共享数据使用,使用过程中数据长度固定不会伸缩。
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_slab的使用
ngx_shmem是一层极简的封装,实现了共享内存的基本功能。但我们程序中大部分的场景共享数据并不会一个固定大小的结构,而更多是像ngx_array、ngx_list、ngx_queue、ngx_rbtree这类大小可以变化的数据结构。
我们期望能有像ngx_pool_t一样可以动态申请释放空间一个内存池。ngx_slab正是一个这样的结构体,原理上与系统的malloc()有相识之处都是通过一系列算法实现对一段段内存片段的申请与释放。只不过ngx_slab操作的对象是基于ngx_shmem的共享内存。
先看一下ngx_slab的接口
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()则直接申请空间,认为程序已经在其他逻辑中获得锁了。
在nginx的开发中使用ngx_shmem一般需要遵循以下初始化流程:
- 模块在配置解析过程中调用ngx_shared_memory_add()接口,注册一段共享内存。提供共享内存大小与内存初始化的回调函数。
- 框架在ngx_init_cycle()中使用ngx_shmem申请内存,并初始化ngx_slab,然后回调模块注册的初始化函数
- 模块使用ngx_slab的申请/是否接口
在这个流程中,涉及到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(第2版) https://book.douban.com/subject/26745255/
ngx_http_limit_conn_module http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
nginx,共享内存
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。
更新日志
- 杨千嬅《如果大家都拥有海》寰亚 [WAV+CUE][998M]
- 孟庭苇.1994-1990-1994钻石精选集2CD(2022环球XRCD限量版)【上华】【WAV+CUE】
- 群星.1998-华纳好情歌精选17首【华纳】【WAV+CUE】
- 张敬轩王菀之.2006-903.ID.CLUB拉阔演奏厅LIVE.2CD【环球】【WAV+CUE】
- 《欢欣森活》存档方法
- 《炉石传说》2024新赛季上线更新内容问题汇总
- 《南瓜恐慌》进不去游戏解决方法
- 杨烁《杨烁唱唐诗》2024Hi-Res[WAV分轨]
- 杨烁《杨烁唱唐诗》2024Hi-Res[WAV分轨]
- 童丽《民歌童丽(HQCD)》【WAV+CUE】
- 童丽《绝对收藏》2022头版限量编号[WAV+CUE][1G]
- 腾格尔《出走天堂》MQA-UHQCD限量版[低速原抓WAV+CUE][1G]
- 田震《时光音乐会》纯银CD[低速原抓WAV+CUE][1G]
- 炉石传说11月初最强登顶卡组合集 炉石传说11月初登顶卡组分享
- lol炼金龙魂详细属性是什么 2024炼金龙魂详细属性介绍