从应用程序的启动过程中main功能开始跟踪。


解析命令行參数并保存到ngx_cycle_t结构体中,在ngx_process_options函数中将保存配置文件路径。

调用ngx_add_inherited_sockets函数获取环境变量中关于平滑升级的一些信息。平滑升级时,旧的master进程会通过环境变量发送传递一些信息给新的master进程,新的master进程启动后要去环境变量中取得这些信息,这就是这个函数的作用。

调用ngx_init_cycle初始化ngx_cycle_t结构体,当中包含:
  • 调用每个核心模块的create_conf方法创建存储配置项參数的结构体。

    注意,非核心模块存储配置项的结构体不在这里创建,而是由核心模块负责创建。

  • 调用配置模块提供的解析方法,遍历配置文件。对于每个配置项又遍历全部核心模块,核心模块感兴趣的配置项是存放在ngx_command_t结构体中的。找出对该配置项感兴趣的合兴模块后调用ngx_command_t中定义的解析方法。
  • 调用全部核心模块的init_conf方法,依据解析的配置项參数进行初始化。

  • 创建并打开所需的文件夹和文件。
  • 初始化用于进程间通信的共享内存。

  • 调用ngx_open_listening_sockets函数打开由各Nginx模块从配置文件里读取到的监听port。

    全部的监听port都保存在ngx_cycle_t->listening数组中。存储元素是ngx_listening_t结构体。表示监听port及相关參数。

  • 调用全部模块的init_module方法,这种方法保存在ngx_module_t->init_module函数指针中。
假设配置文件设为单进程工作模式(master_process为off),将调用ngx_single_process_cycle函数。在该函数中调用全部模块的init_process方法,该方法保存在ngx_module_t->init_process函数指针中。系统进入单进程模循环工作。


假设配置文件设为master-worker工作模式(master_process为on),则调用ngx_master_process_cycle函数。该函数fork(封装在ngx_start_worker_processes函数中)出work子进程。work进程会进入ngx_worker_process_cycle函数。该函数首先调用ngx_worker_process_init方法,后者调用全部模块的init_process方法。然后work进程在ngx_worker_process_cycle中循环工作。

再来一张总体流程图:


worker进程工作流程
worker进程在ngx_worker_process_cycle函数的for循环中不断执行。它检查四个标志位:
  • ngx_terminate:表示强制关闭进程。由信号处理函数ngx_signal_handler函数置1.
  • ngx_quit:表示优雅关闭进程,由信号处理函数ngx_signal_handler函数置1.
  • ngx_reopen:表示又一次打开文件,由信号处理函数ngx_signal_handler函数置1.
  • ngx_exiting:表示将要退出进程,仅在为0且ngx_quit被置的情况下置1.
标志位和信号相应关系例如以下图:


worker进程通过检查以上四个标志位来决定程序运行流。我把整个函数做了简化。代码例如以下:
static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
ngx_int_t worker = (intptr_t) data;
ngx_uint_t i;
ngx_connection_t *c;
/* 该函数内会调用全部模块的init_process方法 */
ngx_worker_process_init(cycle, worker);
...
for ( ;; )
{
/* 退出进程标识 */
if (ngx_exiting)
{
c = cycle->connections;
/* 关闭全部连接 */
for (i = 0; i < cycle->connection_n; i++)
{
if (c[i].fd != -1 && c[i].idle)
{
c[i].close = 1; /* close标识置1 */
c[i].read->handler(c[i].read); /* 调用读事件处理方法 */
}
}
if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
{
/* 红黑树为空,已经处理全然部事件。调用全部模块的exit_process方法 */
ngx_worker_process_exit(cycle);
}
}
/* 还有事件须要处理,继续向下运行 */
ngx_process_events_and_timers(cycle); /* 处理事件,事件模块核心方法 */
/* 强制关闭进程 */
if (ngx_terminate)
ngx_worker_process_exit(cycle); /* 直接调用全部模块的exit_process方法退出worker进程 */
/* 优雅的关闭连接 */
if (ngx_quit)
{
ngx_quit = 0;
ngx_setproctitle("worker process is shutting down"); /* 改动进程名字 */
if (!ngx_exiting)
{
ngx_close_listening_sockets(cycle); /* 关闭监听句柄 */
ngx_exiting = 1; /* ngx_exiting标识唯一被改动的地方 */
}
}
if (ngx_reopen)
{
/* 又一次打开全部文件 */
ngx_reopen = 0;
ngx_reopen_files(cycle, -1);
}
}
}

流程图例如以下:




master进程工作流程
master进程在函数ngx_master_process_cycle中不断循环。它不处理网络事件,它仅仅是管理worker子进程。

和worker进程类似。master通过监控七个标志位来决定程序运行流。这七个标志位例如以下所看到的:


下面是经过简化的ngx_master_process_cycle函数:
void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{
...
/* 依据配置文件启动worker_processes个work子进程 */
ngx_start_worker_processes(cycle, ccf->worker_processes,
NGX_PROCESS_RESPAWN);
/* master进程 */
for ( ;; )
{
sigsuspend(&set); /* 堵塞,等待信号 */
if (ngx_reap)
{
ngx_reap = 0;
/* 监控全部子进程,又一次启动非正常退出的子进程 */
live = ngx_reap_children(cycle);
}
/* live为0表示全部子进程已经退出 */
if (!live && (ngx_terminate || ngx_quit))
{
/* 满足上述条件,退出master进程,包含:
* 1、删除存储进程的pid文件
* 2、调用全部模块的exit_master方法
* 3、关闭监听port
* 4、销毁内存池
*/
ngx_master_process_exit(cycle);
}
if (ngx_terminate) /* 强制关闭 */
{
if (delay > 1000)
{
ngx_signal_worker_processes(cycle, SIGKILL); /* SIGKILL = 9 */
}
else
{
/* NGX_TERMINATE_SIGNAL = TERM = 15
* 向每一个子进程发送TERM信号
*/
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_TERMINATE_SIGNAL));
}
continue; /* 跳上去挂起 */
}
if (ngx_quit) /* 优雅的退出。向全部子进程发送QUIT信号 */
{
ngx_signal_worker_processes(cycle,
ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); /* NGX_SHUTDOWN_SIGNAL = QUIT */
ls = cycle->listening.elts;
/* 关闭全部port */
for (n = 0; n < cycle->listening.nelts; n++)
ngx_close_socket(ls[n].fd);
cycle->listening.nelts = 0;
continue; /* 跳上去挂起 */
}
if (ngx_reconfigure) /* 须要又一次读取配置文件 */
{
/* 又一次读取配置文件后,生成新的worker进程,销毁旧的worker进程 */
ngx_reconfigure = 0;
cycle = ngx_init_cycle(cycle); /* 又一次配置ngx_cycle_t结构体 */
ngx_cycle = cycle;
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
/* 又一次派生子进程 */
ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN);
ngx_start_cache_manager_processes(cycle, 1);
live = 1; /* 表示有子进程在执行 */
/* 向旧的子进程发送QUIT信号,要求它们退出 */
ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
if (ngx_restart) /* 重新启动work进程 */
{
ngx_restart = 0;
ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN);
ngx_start_cache_manager_processes(cycle, 0);
live = 1; /* 表示有子进程在执行 */
}
if (ngx_reopen) /* 又一次打开全部文件 */
{
ngx_reopen = 0;
ngx_reopen_files(cycle, ccf->user);
/* 向全部子进程发送USR1信号,要求每一个子进程又一次打开文件 */
ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_REOPEN_SIGNAL));
}
if (ngx_change_binary) /* 平滑升级 */
{
ngx_change_binary = 0;
ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv); /* 新的子进程启动新版本号的Nginx */
}
if (ngx_noaccept) /* 优雅的关闭服务 */
{
ngx_noaccept = 0;
ngx_noaccepting = 1;
/* 向全部子进程发送QUIT信号,要求它们优雅的关闭服务 */
ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
}
}
}
流程图例如以下:



參考:
《深入理解Nginx》 P275-P286.

版权声明:本文博主原创文章,博客,未经同意不得转载。

【Nginx】启动过程的更多相关文章

  1. Nginx学习之十一-Nginx启动框架处理流程

    Nginx启动过程流程图 下面首先给出Nginx启动过程的流程图: ngx_cycle_t结构体 Nginx的启动初始化在src/core/nginx.c的main函数中完成,当然main函数是整个N ...

  2. 菜鸟nginx源码剖析 框架篇(一) 从main函数看nginx启动流程(转)

    俗话说的好,牵牛要牵牛鼻子 驾车顶牛,处理复杂的东西,只要抓住重点,才能理清脉络,不至于深陷其中,不能自拔.对复杂的nginx而言,main函数就是“牛之鼻”,只要能理清main函数,就一定能理解其中 ...

  3. 【nginx】【转】Nginx启动框架处理流程

    Nginx启动过程流程图: ngx_cycle_t结构体: Nginx的启动初始化在src/core/nginx.c的main函数中完成,当然main函数是整个Nginx的入口,除了完成启动初始化任务 ...

  4. 菜鸟nginx源代码剖析 框架篇(一) 从main函数看nginx启动流程

    菜鸟nginx源代码剖析 框架篇(一) 从main函数看nginx启动流程 Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.c ...

  5. Nginx学习笔记(六) 源码分析&启动过程

    Nginx的启动过程 主要介绍Nginx的启动过程,可以在/core/nginx.c中找到Nginx的主函数main(),那么就从这里开始分析Nginx的启动过程. 涉及到的基本函数 源码: /* * ...

  6. Nginx的启动过程

    主要介绍Nginx的启动过程,可以在/core/nginx.c中找到Nginx的主函数main(),那么就从这里开始分析Nginx的启动过程. 涉及到的基本函数 源码:  View Code Ngin ...

  7. nginx启动过程分析

    nginx的启动过程紧紧环绕着ngx_cycle_t的结构体展开,首先通过ngx_get_options()获取命令行參数.然后通过ngx_time_init()进行时间的初始化.如全局变量ngx_c ...

  8. Nginx学习——Nginx启动、停止、重启和信号控制以及平滑升级

    1.Nginx 启动与停止 (1)启动方式 启动格式:Nginx可执行文件地址 -c Nginx配置文件地址 /etc/local/nginx/sbin/nginx -c /root/dufy/ngi ...

  9. linux环境手动编译安装Nginx实践过程 附异常解决

    1.下载nginx源码包并解压 可在http://nginx.org/en/download.html下载.tar.gz的源码包,如(nginx-1.4.7.tar.gz) 或者使用云盘下载   ht ...

随机推荐

  1. 《Swift编程语言》中文翻译及读书笔记page25

    The Swift Programming Language读书笔记学习笔记 第25页 本页主要说在swift语言里能够使用分号,但分号不作为每条swift语言语句的结尾 而是间隔写在一行的多条swi ...

  2. CF卡技术详解——笔记

    知识太全面了,摘抄摘不完,还是粘过来加上注释和笔记吧. 重点以及断句用加粗,注释用红括号. 一.CF卡技术及规格 一.CF卡技术及规格 1.CF卡简史 随着数码产品的高速普及,近年来闪存卡也进入了高速 ...

  3. SetWindowLong

    SetWindowLong函数介绍 收藏 SetWindowLong函数介绍 SetWindowLong Unicode 函数原型 LONG SetWindowLong(hwnd,nIndex,lNe ...

  4. SQL server语句练习

    相关表: <span style="white-space:pre">create table DEPT ( <span style="white-sp ...

  5. “新浪UC”的后江湖时代------易名新浪SHOW重出江湖

        说到新浪UC,相信很多老网民应该并不陌生,当年QQ放号收费让新浪UC火爆了好一阵子,而随着QQ的崛起,UC也就渐渐退出了即时通信市场,不过,这并不意味着新浪UC退出了历史舞台,因为目前炙手可热 ...

  6. HUNNU11342:Chemistry(模拟)

    http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11342 Problem description The ch ...

  7. 眼见为实(2):介绍Windows的窗口、消息、子类化和超类化

    眼见为实(2):介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较“生僻”的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进程和 ...

  8. 与众不同 windows phone (12) - Background Task(后台任务)之 PeriodicTask(周期任务)和 ResourceIntensiveTask(资源密集型任务)

    原文:与众不同 windows phone (12) - Background Task(后台任务)之 PeriodicTask(周期任务)和 ResourceIntensiveTask(资源密集型任 ...

  9. 5350.support

    3G6200N3G6200NL3G300MAIR3GIIALL02393GALL0256NALL5002ALL5003ARGUS_ATP52B,ASL26555AWM002EVBAWAPN2403BC ...

  10. AJAX入门---DOM操作HTML

    AJAX入门---DOM操作HTML 一边学习AJAX一边坐着总结加深印象.上次写的是怎样简单的使用XMLHttpRequest对象.今天写的是DOM(文档对象模型(Document Object M ...