Nginx源码分析:3张图看懂启动及进程⼯作原理
编者按:⾼可⽤架构分享及传播在架构领域具有典型意义的⽂章,本⽂由陈科在⾼可⽤架构
分享。转载请注明来⾃⾼可⽤架构「ArchNotes」。
导读:很多⼯程师及架构师都希望了解及掌握⾼性能服务器开发,阅读优秀源代码是⼀种有效
的⽅式,nginx 是业界知名的⾼性能 Web 服务器实现,如何有效的阅读及理解 nginx?本⽂⽤
图解的⽅式帮助⼤家来更好的阅读及理解 nginx 关键环节的实现。
陈科,⼗年⾏业从业经验,曾在浙江电信、阿⾥巴巴、华为、五⼋同城任开发⼯程
及架构师等职,⽬前负责河狸家后端架构和运维。博客地址:
www.dumpcache/wiki/doku.php
图⼀:nginx 启动及内存申请过程分析
任何程序都离不开启动和配置解析。ngx 的代码离不开 ngx_cycle_s 和 ngx_pool_s 这两个核⼼
数据结构,所以我们在启动之前先来分析下。
内存申请过程分为 3 步
1. 假如申请的内存⼩于当前块剩余的空间,则直接在当前块中分配。
2. 假如当前块空间不⾜,则调⽤ ngx_palloc_block 分配⼀个新块然后把新块链接到 d.next
中,然后分配数据。
3. 假如申请的⼤⼩⼤于当前块的最⼤值,则直接调⽤ ngx_palloc_large 分配⼀个⼤块,并且
链接到 pool→large 链表中
内存分配过程图解如下
(图⽚来⾃⽹络)
为了更好理解上⾯的图,可以参看⽂末附 2 的⼏个数据结构:ngx_pool_s 及 ngx_cycle_s。
知道了这两个核⼼数据结构之后,我们正式进⼊ main 函数,main 函数执⾏过程如下
调⽤ ngx_get_options() 解析命令参数;
调⽤ ngx_time_init() 初始化并更新时间,如全局变量ngx_cached_time;
调⽤ ngx_log_init() 初始化⽇志,如初始化全局变量 ngx_prefix,打开⽇志⽂件
ngx_log_file.fd;
清零全局变量 ngx_cycle,并为 ngx_cycle.pool 创建⼤⼩为 1024B 的内存池;
调⽤ ngx_save_argv() 保存命令⾏参数⾄全局变量 ngx_os_argv、ngx_argc、ngx_argv 中;
调⽤ ngx_process_options() 初始化 ngx_cycle 的 prefix, conf_prefix, conf_file, conf_param 等
字段;
调⽤ ngx_os_init() 初始化系统相关变量,如内存页⾯⼤⼩ ngx_pagesize , ngx_cacheline_size ,最⼤连接数 ngx_max_sockets 等;
调⽤ ngx_crc32_table_init() 初始化 CRC 表 ( 后续的 CRC 校验通过查表进⾏,效率⾼ );
调⽤ ngx_add_inherited_sockets() 继承 sockets:
解析环境变量 NGINX_VAR = 'NGINX' 中的 sockets,并保存⾄ ngx_cycle.listening 数组;
设置 ngx_inherited = 1;
调⽤ ngx_set_inherited_sockets() 逐⼀对 ngx_cycle.listening 数组中的 sockets 进⾏设置;
初始化每个 module 的 index,并计算 ngx_max_module;
调⽤ ngx_init_cycle() 进⾏初始化;
该初始化主要对 ngx_cycle 结构进⾏;
若有信号,则进⼊ ngx_signal_process() 处理;
调⽤ ngx_init_signals() 初始化信号;主要完成信号处理程序的注册;
若⽆继承 sockets,且设置了守护进程标识,则调⽤ ngx_daemon() 创建守护进程;
调⽤ ngx_create_pidfile() 创建进程记录⽂件;( ⾮ NGX_PROCESS_MASTER = 1 进程,不创建该⽂件 )
进⼊进程主循环;
若为 NGX_PROCESS_SINGLE=1模式,则调⽤ ngx_single_process_cycle() 进⼊进程循环;
否则为 master-worker 模式,调⽤ ngx_master_process_cycle() 进⼊进程循环;
在 main 函数执⾏过程中,有⼀个⾮常重要的函数 ngx_init_cycle,这个阶段做了什么呢?下⾯分析 ngx_init_cycle,初始化过程:
1. 更新 timezone 和 time
2. 创建内存池
3. 给 cycle 指针分配内存
4. 保存安装路径,配置⽂件,启动参数等
5. 初始化打开⽂件句柄
6. 初始化共享内存
7. 初始化连接队列
8. 保存 hostname
9. 调⽤各 NGX_CORE_MODULE 的 create_conf ⽅法
10. 解析配置⽂件
11. 调⽤各NGX_CORE_MODULE的init_conf⽅法
12. 打开新的⽂件句柄
13. 创建共享内存
14. 处理监听socket
15. 创建socket进⾏监听
16. 调⽤各模块的init_module
图⼆:master 进程⼯作原理及⼯作⼯程
以下过程都在ngx_master_process_cycle 函数中进⾏,启动过程:
1. 暂时阻塞所有 ngx 需要处理的信号
2. 设置进程名称
3. 启动⼯作进程
4. 启动cache管理进程
5. 进⼊循环开始处理相关信号
master 进程⼯作过程
1. 设置 work 进程退出等待时间
2. 挂起,等待新的信号来临
3. 更新时间
4. 如果有 worker 进程因为 SIGCHLD 信号退出了,则重启 worker 进程
5. master 进程退出。如果所有 worker 进程都退出了,并且收到 SIGTERM 信号或 SIGINT
信号或 SIGQUIT 信号等,master 进程开始处理退出
6. 处理SIGTERM信号
7. 处理SIGQUIT信号,并且关闭socket
8. 处理SIGHUP信号
1. 平滑升级,重启worker进程
2. 不是平滑升级,需要重新读取配置
9. 处理重启 10处理SIGUSR1信号重新打开所有⽂件 11处理SIGUSR2信号热代码替换,执
⾏新的程序 12处理SIGWINCH信号,不再处理任何请求
图三:worker 进程⼯作原理
启动通过执⾏ ngx_start_worker_processes 函数:
1. 先在 ngx_processes 数组中坑位if (ngx_processes[s].pid == -1) {break;}
2. 进程相关结构初始化⼯作
1. 创建管道 ( socketpair )
2. 设置管道为⾮阻塞模式
3. 设置管道为异步模式
4. 设置异步 I/O 的所有者
5. 如果 exec 执⾏的时候本 fd 不传递给 exec 创建的进程
3. fork 创建⼦进程。创建成功后,⼦进程执⾏相关逻辑:proc(cycle, data)。
4. 设置 ngx_processes[s] 相关属性
5. 通知⼦进程新进程创建完毕 ngx_pass_open_channel(cycle, &ch);
接下来是 ngx_worker_process_cycle worker 进程逻辑
1. ngx_worker_process_init
php的工作流程
1. 初始化环境变量
2. 设置进程优先级
3. 设置⽂件句柄数量限制
4. 设置 core_file ⽂件
5. ⽤户组设置
6. cpu 亲和度设置
7. 设定⼯作⽬录
8. 设置随机种⼦数
9. 初始化监听状态
10. 调⽤各模块的init_process⽅法进⾏初始化
11. 关闭别⼈的fd[1],保留别⼈的fd[1]⽤于互相通信。⾃⼰的fd[1]接收master进程的消息。
12. 监听channel读事件
2. 进程模式
1. 处理管道信号。这个过程由 ngx_channel_handler 完成,这部分具体实现在管道事
件中讲解。
3. 线程模式
1. ngx_worker_thread_cycle 是⼀个线程的循环:死循环中除了处理退出信号。主要进
⾏ngx_event_thread_process_posted⼯作,这块具体内容在后⾯讲事件模型的时候再
展开。
4. 处理相关信号
master 和 worker 通信原理为:
Nginx 事件机制介绍
先看⼏个主要⽅法
ngx_add_channel_event 主要是把事件注册到事件池中,并且添加事件 handler,具体要结合后⾯的事件机制来展开。
ngx_write_channel 主要是将数据写⼊到 pipe 中:
n = sendmsg(s, &msg, 0);
Top of Form
Bottom of Form
ngx_read_channel 从 pipe 中读取数据:n = recvmsg(s, &msg, 0);
接下来分析事件模块⼯作流程
ngx_event模块结构
ngx_events_module 的数据结构如下:
ngx_module_t ngx_events_module = {
NGX_MODULE_V1,
&ngx_events_module_ctx, /* module context */
ngx_events_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_event 模块初始化
static ngx_command_t ngx_events_commands[] = {
{
ngx_string('events') ,
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS ,
ngx_events_block, 0, 0, NULL
},
ngx_null_command
};

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。