我是靠谱客的博主 彩色篮球,最近开发中收集的这篇文章主要介绍Nginx源码分析:3张图看懂启动及进程工作原理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

图一: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

    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源码分析:3张图看懂启动及进程工作原理的全部内容,希望文章能够帮你解决Nginx源码分析:3张图看懂启动及进程工作原理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部