《深入剖析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处理信号时,根据尽量简单的原则,只进行少量工作

信号处理完成后,根据旗标进行真正处理

posted on 2022-03-11 13:54  开心种树  阅读(1004)  评论(0编辑  收藏  举报