《深入剖析ngx》——进程模型
1. 进程工作
ngx是多进程模型:work进程,master进程,cache进程。
ngx还使用 upstream等模块实现和 其他服务器通信
master和worker进程工作流程如下
可以看出 用户使用信号控制 监控进程,监控进程 控制工作进程
监控进程主要处理 用户的信号,
监控进程使用 sigsuspend 阻塞等待信号,信号处理函数设置旗标,根据旗标完成具体动作。
ngx_quit 会进行清理工作再退出。
ngx_terminate 使用 SIGKILL 让自己退出
ngx_reconfigure 重新加载配置
工作进程主要处理和 客户端和后端服务器 IO,使用 select epoll 等多路复用实现。
2. 进程通信
2.1 channel
-
父子通信
ngx使用 socketpair 分配 两个互相连接的 Unix套接字,fork后,channel[0] <----> channel[1] 依旧连接,实现血缘关系进程通信。 -
兄弟通信
新的子进程会继承老的子进程的已连接的套接字,但是 老的子进程没有用于写给新子进程的套接字,ngx利用 进程间套接字传递 实现兄弟通信。
进程间传递文件描述符,参考:
https://www.cnblogs.com/yangxinrui/p/16083105.html
- 总结
虽然 ngx 实现了 channel 机制,可以完成 父子兄弟互相通信,但只用 channel 进行 控制进程对工作进程的通信。
而子进程之间使用更好的通信方法,如共享内存。
2.2 共享内存
2.2.1 初始化
ngx的共享内存可以通过 用户配置 动态申请,如
这里申请一个共享内存,10M,名称为one,
ngx使用 ngx_shm_zone_s 描述内存块信息。
16 typedef struct {
17 u_char *addr;
18 size_t size;
19 ngx_str_t name;
20 ngx_log_t *log;
21 ngx_uint_t exists; /* unsigned exists:1; */
22 } ngx_shm_t;
25 typedef struct ngx_shm_zone_s ngx_shm_zone_t;
26
27 typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data);
28
29 struct ngx_shm_zone_s {
30 void *data;
31 ngx_shm_t shm;
32 ngx_shm_zone_init_pt init;
33 void *tag;
34 void *sync;
35 ngx_uint_t noreuse; /* unsigned noreuse:1; */
36 };
shm.shm.name 是内存块名称,是内存块唯一键值
shm.tag 用于区分内存块的创建和使用,如 模块A 创建名称为a的内存块,之后模块A再用名称a从内存池中获取内存块,这是可行的,但是若模块B也创建名称为a的内存块,若内存块只有name做区分,则模块B也能获得内存块a,但这导致冲突,所以需要tag属性表明该内存块的owner,如此模块B再创建名称为a的内存块时,还会比较tag是否为模块B,匹配失败则报错,模块B只能重建其他名称内存块。
ngx解析完配置文件时,调用 shared_memory_add() 创建 ngx_shm_zone_s,ngx_shm_zone_s只是内存块描述,不是真正的内存块,并将ngx_shm_zone_s 加入 链表 cf->cycle->shared_memory ,检查冲突后,遍历链表 分配共享内存,并格式化内存块。
ngx_shm_alloc 是对 系统调用的封装,完成内存块申请
ngx_init_zone_pool 对内存块进行格式化
shm_zone[i].init 回调用户的初始化方法
以 ngx_http_limit_req_module 的初始化方法为例
若旧数据可用(重加载配置情况),则直接返回,否则初始化模块ctx,用slab方法申请内存。
以上完成内存块初始化,关于内存块的使用涉及 互斥和slab
2.2.2 互斥
互斥是使用锁解决,ngx互斥锁接口如下
2.2.3 slab
ngx实现的slab是二级页表,支持分配和释放内存。
在共享内存的头部,有头信息
一级页表
page[] 是真实用于分配的内存块,
ngx_slab_page_t[] ngx管理page使用的描述结构,ngx_slab_page_t 和 page 一一对应,获得 ngx_slab_page_t 地址,就能求得page地址。
ngx 对 ngx_slab_page_t 的管理,是使用 链表,链表元素为数组。
ngx_slab_page_t 数组的首元素的 slab属性,表示本数组的长度
当分配空间如下
释放空间,回收内存块
二级页表,
每个 ngx_slab_page_t 的二级页面粒度不同,对 其对应 page 进行划分。并用位图方式描述使用情况。
如果粒度能用32bit表示,则使用 ngx_slab_page_t.slab属性表示
如果粒度用不完32bit,则使用 ngx_slab_page_t.slab属性的一部分bit位表示
如果粒度用32bit表示不完,则使用 page首部表示
3. 信号
ngx对信号声明对象
静态定义信号对象数组
其中涉及的C语言宏
在初始化时,完成信号处理的初始化
ngx处理信号时,根据尽量简单的原则,只进行少量工作
信号处理完成后,根据旗标进行真正处理
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?