盗用前面用到的流程图
第二步实际上是调用
ngx_add_inherited_sockets()
//文件名: Nginx.c
int ngx_cdecl
main(int argc, char *const *argv)
{
...
if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {
return 1;
}
...
}
Nginx在不重启服务升级,旧版本的
master
进程会通过execve
系统调用来启动新版本的master
进程(先 fork()出子进程再调用exec来运行新程序),这时旧版本 的master
进程通过 环境变量通知新版本的master
这是在升级,新版本的master
进程通过ngx_add_inherited_sockets()
方法 由环境变量里读取平滑升级信息,并对旧版本Nginx服务监听 的句柄做继承处理 。
3~8步都在
ngx_init_cycle()
方法中执行的。在初始化ngx_cycle_t
中所有的容器后,会为读取、解析文件 做准备工作。因为每个 模块都必须有相应的数据结构来 存储配置文件中的各配置项,创建这些数据结构的工作都需要在这一步进行。Nginx框架只关心NGX_CORE_MODULE
核心模块,从而降低框架的复杂度,这里将会调用所有核心模块的create_conf
的方法(也只有核心模块才有这个方法 ),这意味着所有核心模块开始构造用于存储配置项的结构体。非核心模块将由每个模块进行管理,如 HTTP模块都由ngx_http_module
管理。这样ngx_http_module
在解析自己感兴趣的”http”配置项时,将会调用所有HTTP模块 约定的方法来 创建存储配置的结构体
//文件名 ngx_cycle.h
typedef struct ngx_cycle_s ngx_cycle_t;
struct ngx_cycle_s {
void ****conf_ctx; // 配置上下文数组(含所有模块)
ngx_pool_t *pool; // 内存池
ngx_log_t *log; // 日志
ngx_log_t new_log;
ngx_connection_t **files; // 连接文件
ngx_connection_t *free_connections; // 空闲连接
ngx_uint_t free_connection_n; // 空闲连接个数
ngx_module_t **modules;
ngx_uint_t modules_n;
ngx_uint_t modules_used; /* unsigned modules_used:1; */
ngx_queue_t reusable_connections_queue; // 再利用连接队列
ngx_array_t listening; // 监听套接字数组
ngx_array_t pathes; // 路径数组
ngx_list_t open_files; // 打开文件链表
ngx_list_t shared_memory; // 共享内存链表
ngx_uint_t connection_n; // 连接个数
ngx_uint_t files_n; // 打开文件个数
ngx_connection_t *connections; // 连接
ngx_event_t *read_events; // 读事件
ngx_event_t *write_events; // 写事件
ngx_cycle_t *old_cycle; // old cycle指针
ngx_str_t conf_file; // 配置文件
ngx_str_t conf_param; // 配置参数
ngx_str_t conf_prefix; // 配置文件目录
ngx_str_t prefix; // 程序工作目录
ngx_str_t lock_file; // 锁文件,用在不支持accept_mutex的系统中
ngx_str_t hostname; // 主机名
};
调用配置模块提供的解析配置项方法 。遍历
nginx.conf
的所有配置项,对于任一配置项,将会检查所有核心模块以找出对它所感兴趣的模块。并调用该模块在ngx_command_t
结构体中的定义的 配置项处理办法 。
调用所有的NGX_CORE_MODULE
核心模块的init_conf
的 方法。这一步骤的 目的在于让所有核心模块在解析完配置项可以做综合性处理。
之前第四步在 解析配置项时,所有的模块都已经解析出自己需要监听的端口 ,如HTTP模块 已经 在 解析http{…}配置项时得到要监听的端口 ,并添加到listening数组中。这一步骤就是按照listening数组中的每一个ngx_listening_t
元素设置socket句柄并监听端口,实际上就是调用ngx_open_listening_sockets()
。
在这个阶段会调用所有模块的init_module
方法。接下来 就是根据 配置Nginx运行模式决定如何工作。
接下来流程可以参考之前的博客nginx代码分析
worker进程工作流程
master
采用的是信号的方式通知worker进程停止服务或更换日志。在函数ngx_worker_process_cycle()
通过检查ngx_exiting、ngx_terminate、ngx_quit、ngx_reopen
这4个标志位来决定后续动作。
master进程工作流程
master
进程不需要处理网络事件,不负责业务的执行,只会通过该管理worker
等子进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等 功能 。 master
进程中所有子进程相关的状态信息都保存在ngx_processes数组中,下面 是数组元素的类型ngx_process_t
的 结构的定义,代码如下 :
typedef struct {
//进程 ID
ngx_pid_t pid;
// 由waitpid系统调用获取到进程状态
int status;
// 这是由socketpair系统调用产生出用于进程 间通信 的socket句柄
ngx_socket_t channel[2];
// 子进程的循环执行的办法,当父进程调用ngx_spawn_process 生成子进程时使用
ngx_spawn_proc_pt proc;
void *data;
// 进程名称。操作系统中显示的进程名称与name相同
char *name;
// 标志位,为1时表示在重新生成子进程
unsigned respawn:1;
// 标志位, 为1时表示正在 生成子进程
unsigned just_spawn:1;
// 标志位,为1时表示在父进程、子进程分离
unsigned detached:1;
// 标志位,为1时表示进程正在退出
unsigned exiting:1;
// 标志位,为1时表示进程已经退出
unsigned exited:1;
} ngx_process_t;
ngx_spawn_process
方法封装了fork
系统调用,并且会从ngx_processes
数组中选择一个还未使用的ngx_process_t
元素存储这个子进程的相关信息。