【Nginx】启动流程

本流程从启动应用程序后的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模块从配置文件中读取到的监听端口。所有的监听端口都保存在ngx_cycle_t->listening数组中,存储元素是ngx_listening_t结构体,表示监听端口及相关参数。
  • 调用所有模块的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、关闭监听端口
             *      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;
            /* 关闭所有端口 */
            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】启动流程

时间: 2024-08-24 21:49:37

【Nginx】启动流程的相关文章

Nginx启动流程概览

Nginx启动流程概览 1. Init Cycle 2. Init Process 3. Start Worker 4. Event Handle Init Cycle ngx_cycle 是nginx全局配置,类型为ngx_cycle_t,其结构如下(已精简): struct ngx_cycle_s { void ****conf_ctx; //全局配置项 ngx_pool_t *pool; ngx_log_t *log; ngx_connection_t **files; ngx_conne

nginx学习十一 nginx启动流程

今天用了一天的时间看nginx的启动流程,流程还是很复杂,主要的函数调用有十几个之多,通过看源码和上网查资料,弄懂了一些函数,有些函数还在学习中,有些函数还待日后学习,这里记录一下今天所学.加油! http://blog.csdn.net/xiaoliangsky/article/details/39856803 1nginx.c 启动的程序主要在src/core/nginx.c中,和普通函数一样,main函数是其入口函数:下面我们看看main函数的源代码: int ngx_cdecl main

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

俗话说的好,牵牛要牵牛鼻子 驾车顶牛,处理复杂的东西,只要抓住重点,才能理清脉络,不至于深陷其中,不能自拔.对复杂的nginx而言,main函数就是“牛之鼻”,只要能理清main函数,就一定能理解其中的奥秘,下面我们就一起来研究一下nginx的main函数. 1.nginx的main函数解读 nginx启动显然是由main函数驱动的,main函数在在core/nginx.c文件中,其源代码解析如下,涉及到的数据结构在本节仅指出其作用,将在第二节中详细解释. nginx main函数的流程图如下:

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

Nginx启动过程流程图: ngx_cycle_t结构体: Nginx的启动初始化在src/core/nginx.c的main函数中完成,当然main函数是整个Nginx的入口,除了完成启动初始化任务以外,也必定是所有功能模块的入口之处.Nginx的初始化工作主要围绕一个类型为ngx_cycle_t类型的全局变量(cycle)展开. ngx_cycle_t结构体类型: typedef struct ngx_cycle_s ngx_cycle_t; struct ngx_cycle_s { voi

Nginx(一):启动流程解析

nginx作为高效的http服务器和反向代理服务器,值得我们深入了解. 我们带着几个问题,深入了解下nginx的工作原理.首先是开篇:nginx是如何启动的? nginx是用c写的软件,github地址: https://github.com/nginx/nginx 其目录结构如下,我们主要关注 src 目录下的文件. nginx.c 是main函数入口,我们也是通过这里进行启动流程分析的. 零.启动流程时序图 我们先通过一个时序图进行全局观察nginx是如何跑起来的,然后后续再稍微深入了解些细

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

Nginx启动过程流程图 下面首先给出Nginx启动过程的流程图: ngx_cycle_t结构体 Nginx的启动初始化在src/core/nginx.c的main函数中完成,当然main函数是整个Nginx的入口,除了完成启动初始化任务以外,也必定是所有功能模块的入口之处.Nginx的初始化工作主要围绕一个类型为ngx_cycle_t类型的全局变量(cycle)展开. ngx_cycle_t结构体类型: typedef struct ngx_cycle_s ngx_cycle_t; struc

[转]nginx启动期都做了哪些事

nginx是个多进程web容器,不同的配置下它的启动方式也是不同的,这里我只说说最典型的启动方式. 它有1个master进程,和多个worker进程(最优配置的数量与CPU核数相关).那么,首先我们要找到main函数,它在src/core/nginx.c文件中.谈到源码了,这时我们先简单看下源码的目录结构吧. nginx主要有下列目录: src/core,这个目录存放了基础的数据结构像LIST.红黑树.nginx字符串,贯穿始终的一些逻辑结构如ngx_cycle_s.ngx_connection

Nginx 启动初始化过程

Nginx 启动过程 Nginx 的启动初始化由 main 函数完成,该函数是整个 Nginx 的入口,该函数完成 Nginx 启动初始化任务,也是所有功能模块的入口.Nginx 的初始化工作主要是一个类型为 ngx_cycle_t 类型的全局变量.main 函数定义在文件:src/?core/?nginx.c Nginx 启动过程如下. 调用 ngx_get_options() 解析命令参数: 显示版本号与帮助信息: 调用 ngx_time_init() 初始化并更新时间: 调用 ngx_lo

nginx 启动,停止和重新加载配置

nginx 启动,停止和重新加载配置 要启动nginx的,运行可执行文件.一旦nginx的启动时,它可以通过与-s参数调用可执行来控制.使用以下语法 nginx -s signal 其中,信号可以是下列之一: stop — fast shutdown quit — graceful shutdown reload — reloading the configuration file reopen — reopening the log files 在配置文件中所作的更改不会被应用,直到命令重新配