博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Nginx】启动过程
阅读量:6069 次
发布时间:2019-06-20

本文共 6707 字,大约阅读时间需要 22 分钟。

从应用程序的启动过程中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 voidngx_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函数:
voidngx_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.

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

你可能感兴趣的文章
H5实现全屏与F11全屏
查看>>
处理excel表的列
查看>>
枸杞子也能控制脂肪肝
查看>>
Excuse me?这个前端面试在搞事!
查看>>
C#数据采集类
查看>>
quicksort
查看>>
检验函数运行时间
查看>>
【BZOJ2019】nim
查看>>
Oracle临时表空间满了的解决办法
查看>>
四部曲
查看>>
LINUX内核调试过程
查看>>
【HDOJ】3553 Just a String
查看>>
Java 集合深入理解(7):ArrayList
查看>>
2019年春季学期第四周作业
查看>>
paip.提升性能---并行多核编程哈的数据结构list,set,map
查看>>
[转]mongodb与mysql相比的优缺点
查看>>
未在本地计算机上注册“Microsoft.Ace.OleDb.12.0”提供程序解决办法
查看>>
PHP and java
查看>>
sharepoint 2010 自定义页面布局
查看>>
〖Linux〗Android NDK调用已编译好的C/C++动态连接库(so文件)
查看>>