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

 

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

 

  • Author:Echo Chen(陈斌)

  • Email:[email protected]

  • Blog:Blog.csdn.net/chen19870707

  • Date:Nov 9th, 2014

     

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

    1.nginx的main函数解读

    nginx启动显然是由main函数驱动的,main函数在在core/nginx.c文件中,其源代码解析如下,涉及到的数据结构在本节仅指出其作用,将在第二节中详细解释。

  • nginx main函数的流程图如下:

    image

    需要说明的:

  • 1) 初始化错误提示列表,以errno为下标,元素就是对应的错误提示信息。

  •    1: if (ngx_strerror_init() != NGX_OK) {
          
       2:     return 1;
       3: }

    2)获取命令行参数,保存在全局变量中,可以设置的命令行参数如下表所示:

  •    1: if (ngx_get_options(argc, argv) != NGX_OK) {
          
       2:       return 1;
       3:   }
    命令行参数 作用
    -h或-? 显示版本信息和help信息
    -v 显示版本信息
    -V 显示nginx版本信息、编译器版本和配置选项信息
    -t 测试配置文件信息是否OK,即检测配置文件语法的正确性,并尝试打开配置文件中所引用到的文件
    -q 在测试配置文件的时候,屏蔽无错误信息,即quiet模式
    -s signal 发送信号到master进程(如stop、quit、reopen、reload)
    -p prefix 设置前缀路径(默认为当前目录)
    -c filename 设置配置文件(默认为conf/nginx.conf)
    -g directives 在配置文件外设置全局的指令

    3)时间、正则表达式和log的初始化。

                 1: ngx_time_init();
                 2:  
                 3: (NGX_PCRE)
                 4: ngx_regex_init();
                 5: if
                 6:  
                 7: ngx_pid = ngx_getpid();
                 8:  
                 9: log = ngx_log_init(ngx_prefix);
                10: if (log == NULL) {
                    
                11:     return 1;
                12: }
                      4) 初始化cycle结构,并创建内存块大小为1024的内存池,内存池创建已经在《菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t》讨论过了,nginx框架就是围绕着ngx_cycle_t结构体来控制运行的,其定义详情请参考下一节。
                 1: ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
                 2: init_cycle.log = log;
                 3: ngx_cycle = &init_cycle;
                 4:  
                 5: init_cycle.pool = ngx_create_pool(1024, log);
                 6: if (init_cycle.pool == NULL) {
                    
                 7:     return 1;
                 8: }
               
              5)  将命令行参数保存到ngx_os_argv、ngx_argc以及ngx_argv这几个全局的变量中。这算是一个备份存储,方便以后master进程做热代码替换之用。
                 1: if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {
                    
                 2:         return 1;
                 3: }
              6)用命令行参数得来的全局变量初始化cycle的conf_prefix(配置文件所在路径的前缀)、prefix(nginx可执行文件所在路径)、conf_file(配置文件名)和conf_param(通过命令行-g选项指定的全局配置信息)。
                 1: if (ngx_process_options(&init_cycle) != NGX_OK) {
                    
                 2:     return 1;
                 3: }
              7)  根据操作系统确定一些参数,信息会被保存到一些全局变量中,如页大小ngx_pagesize, CPU cacheline
                 1: if (ngx_os_init(log) != NGX_OK) {
                    
                 2:     return 1;
                 3: }
              8) 初始化一个做循环冗余校验的表,由此可以看出后续的循环冗余校验将采用高效的查表法
                 1: if (ngx_crc32_table_init() != NGX_OK) {  
                 2:        return 1;  
                 3:    } 
              9)通过环境变量NGINX完成socket的继承,继承来的socket将会放到init_cycle的listening数组中。同时可以读取master进程传递的平滑升级信息等等
                 1: if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {  
                 2:         return 1;  
                 3:     }  

              10)初始化所有模块的index信息,即对所有模块进行编号,ngx_modules数却是在自动编译的时候生成的,位于objs/ngx_modules.c文件中

                 1: ngx_max_module = 0;
                 2: for (i = 0; ngx_modules[i]; i++) {
                    
                 3:     ngx_modules[i]->index = ngx_max_module++;
                 4: }

              11)  用上面收集的init_cycle信息初始化ngx_cycle,这行代码是nginx启动过程中最重要的一个步骤,在第3节将详细展开。

                 1: cycle = ngx_init_cycle(&init_cycle);
                 2: if (cycle == NULL) {
                    
                 3:     if (ngx_test_config) {
                    
                 4:         ngx_log_stderr(0, "configuration file %s test failed",
                 5:                        init_cycle.conf_file.data);
                 6:     }
                 7:  
                 8:     return 1;
                 9: }

               

              12)ccf 为ngx_core_conf_t 将在第2节给出详细定义,这个地方需要解释下,ccf->master是从配置文件中解析master_process配置项所得的值,初始化为NGX_CONF_UNSET(-1),在配置项中,如果flag类型的配置项master_process被设置为on,则其值为1,如果为off,则其值为0,ngx_process为全局变量,用于记录要采用的工作模式,未被初始化,因此初始值是0(uint型全局变量会被系统默认初始化为0),相关宏定义如下:

              1. #define NGX_PROCESS_SINGLE     0
              2. #define NGX_PROCESS_MASTER     1
              3. #define NGX_PROCESS_SIGNALLER  2
              4. #define NGX_PROCESS_WORKER     3
              5. #define NGX_PROCESS_HELPER     4

              因此,下面的if判断语句的含义就是:用来处理一种特殊情况,即如果在配置项中未设置master_process配置项或者是设置为打开,ngx_process未被设置,采用默认值0,这个时候要采用master工作模式。因为master_process优先级高,且nginx默认采用master模式如果在配置项中设置master_process为off,那么if依据不会执行。最终nginx工作模式取决于ngx_proces的初值0,即采用单进程模式。

               

                 1: ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
                 2:  
                 3: if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) {
                    
                 4:     ngx_process = NGX_PROCESS_MASTER;
                 5: }

              13)初始化信号;主要完成信号处理程序的注册

                 1: if (ngx_init_signals(cycle->log) != NGX_OK) {
                    
                 2:     return 1;
                 3: }

              14)若无继承sockets,且设置了守护进程表示,则创建守护进程

                 1: if (!ngx_inherited && ccf->daemon) {  
                 2:         if (ngx_daemon(cycle->log) != NGX_OK) {  
                 3:             return 1;  
                 4:         }  
                 5:   
                 6:         ngx_daemonized = 1;  
                 7:     }  
                 8:   
                 9:     if (ngx_inherited) {  
                10:         ngx_daemonized = 1;  
                11:

              15) 创建进程记录文件;(非NGX_PROCESS_MASTER=1进程,不创建该文件)

                 1: if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {
                    
                 2:     return 1;
                 3: }

              16)  进入进程主循环,根据ngx_process确定启动单进程模式还是多进程模式。

                 1: if (ngx_process == NGX_PROCESS_SINGLE) {
                    
                 2:      ngx_single_process_cycle(cycle);
                 3:  
                 4:  } else {
                    
                 5:      ngx_master_process_cycle(cycle);
                 6:  }
            • 2.相关结构体

              2.1. ngx_module_t

              nginx中所有模块的类型都是ngx_module_t类型的,定义了模块的一些属性。nginx是完全模块化的,所有的组件都是模块,从而实现了nginx的高度松耦合。同时,我们在进行nginx模块开发时,也离不开这个数据结构。在上面初始化过程中的第10步就是初始化这个结构。

                 1: struct ngx_module_s {
                    
                 2:     /**
                 3:      * 在具体类型模块(http、event等)的全局配置结构数组的下标。以http module模块为例,
                 4:      * nginx把所有的http module的config信息存放在ngx_http_conf_ctx_t类型的变量中,
                 5:      * 这个变量只有3个属性,分别是所有http module的main、srv、loc的config信息的数组。
                 6:      * 如果该模块是http module,则ctx_index是该模块的config信息(main、srv、loc)
                 7:      * 在ngx_http_conf_ctx_t中的下标。
                 8:      */
                 9:     ngx_uint_t            ctx_index;
                10:  
                11:     /**
                12:      * nginx把所有模块(ngx_module_t)存放到ngx_modules数组中,这个数组在nginx源码路
                13:      * 径的objs/ngx_modules.c中,是在运行configure脚本后生成的。index属性就是该模块
                14:      * 在ngx_modules数组中的下标。同时nginx把所有的core module的配置结构存放到ngx_cycle的
                15:      * conf_ctx数组中,index也是该模块的配置结构在ngx_cycle->conf_ctx数组中的下标。
                16:      */
                17:     ngx_uint_t            index;
                18:  
                19:     ……
                20:  
                21:     /**
                22:      * 模块的上下文属性,同一类型的模块的属性是相同的,比如core module的ctx是ngx_core_module_t类型。
                23:      * 而http module的ctx是ngx_http_moduel_t类型,event module的ctx是ngx_event_module_t类型等等。
                24:      * 相应类型的模块由分开处理的,比如所有的http module由ngx_http_module解析处理,而所有的event module
                25:      * 由ngx_events_module解析处理。
                26:      */
                27:     void                 *ctx;
                28:  
                29:     /**
                30:      * 该模块支持的指令的数组,最后以一个空指令结尾。ngx_commond_t的分析见下文。
                31:      */
                32:     ngx_command_t        *commands;
                33:  
                34:     /**
                35:      * 模块的类型,nginx所有的模块类型:
                36:      *         NGX_CORE_MODULE
                37:      *         NGX_CONF_MODULE
                38:      *         NGX_HTTP_MODULE
                39:      *         NGX_EVENT_MODULE
                40:      *         NGX_MAIL_MODULE
                41:      * 这些不同的类型也指定了不同的ctx。
                42:      */
                43:     ngx_uint_t            type;
            • 4
              点赞
            • 21
              收藏
              觉得还不错? 一键收藏
            • 5
              评论

            “相关推荐”对你有帮助么?

            • 非常没帮助
            • 没帮助
            • 一般
            • 有帮助
            • 非常有帮助
            提交
            评论 5
            添加红包

            请填写红包祝福语或标题

            红包个数最小为10个

            红包金额最低5元

            当前余额3.43前往充值 >
            需支付:10.00
            成就一亿技术人!
            领取后你会自动成为博主和红包主的粉丝 规则
            hope_wisdom
            发出的红包
            实付
            使用余额支付
            点击重新获取
            扫码支付
            钱包余额 0

            抵扣说明:

            1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
            2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

            余额充值