nginx源码分析--module模块的初始化

在nginx中,为了处理复杂的过程,将其都模块化,来减少耦合,通过拼接组装来处理各种流程,模块化是nginx非常重要的特征。所有的模块都记录在ngx_modules.c中,ngx_modules.c是在编译过程中通过config生成的:

ngx_module_t *ngx_modules[] = {
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    &ngx_regex_module,
    &ngx_events_module,
    &ngx_event_core_module,
    &ngx_epoll_module,
    &ngx_http_module,
    &ngx_http_core_module,
    &ngx_http_log_module,
    &ngx_http_upstream_module,
    &ngx_http_static_module,
    &ngx_http_autoindex_module,
    &ngx_http_index_module,
    &ngx_http_mirror_module,
    &ngx_http_try_files_module,
    &ngx_http_auth_basic_module,
    &ngx_http_access_module,
    &ngx_http_limit_conn_module,
    &ngx_http_limit_req_module,
    &ngx_http_geo_module,
    &ngx_http_map_module,
    &ngx_http_split_clients_module,
    &ngx_http_referer_module,
    &ngx_http_rewrite_module,
    &ngx_http_proxy_module,
    &ngx_http_fastcgi_module,
    &ngx_http_uwsgi_module,
    &ngx_http_scgi_module,
    &ngx_http_memcached_module,
    &ngx_http_empty_gif_module,
    &ngx_http_browser_module,
    &ngx_http_upstream_hash_module,
    &ngx_http_upstream_ip_hash_module,
    &ngx_http_upstream_least_conn_module,
    &ngx_http_upstream_keepalive_module,
    &ngx_http_upstream_zone_module,
    &ngx_http_write_filter_module,
    &ngx_http_header_filter_module,
    &ngx_http_chunked_filter_module,
    &ngx_http_range_header_filter_module,
    &ngx_http_gzip_filter_module,
    &ngx_http_postpone_filter_module,
    &ngx_http_ssi_filter_module,
    &ngx_http_charset_filter_module,
    &ngx_http_userid_filter_module,
    &ngx_http_headers_filter_module,
    &ngx_http_copy_filter_module,
    &ngx_http_range_body_filter_module,
    &ngx_http_not_modified_filter_module,
    NULL
};

ngx_module_s 声明的类型如下:

/**
 * 业务模块数据结构
 */
struct ngx_module_s {
    //ctx_index是表示当前模块在这类模块中的序号,这个成员常常是由管理这类模块的一个nginx核心模块设置的,
    //对于所有http模块而言,ctx_index是由核心模块ngx_http_module设置的。
    ngx_uint_t            ctx_index;
    //index表示当前模块在ngx_modules数组中的序号。注意,ctx_index表示的是当前模块在一类模块中的序号,
    //而index表示当前模块在所有模块中的序号,nginx启动时,会根据ngx_modules数组设置各模块的index值。
    //在 ngx_preinit_modules ()中初始化
    ngx_uint_t            index;             /* 模块的唯一标识符号 */

    char                 *name;              /* 模块名称 */
    //spare系列的保留变量,暂未使用
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    
    ngx_uint_t            version;           /* 模块版本,便于将来扩展,目前,只有一种,默认为1 */
    const char           *signature;
   
    void                 *ctx;               /* 模块上下文 */
    ngx_command_t        *commands;          /* 模块支持的命令集,将处理nginx.conf中的配置项 */
    /*与ctx指针是紧密相关的,在官方nginx中,它的取值范围有5种:
     * NGX_HTTP_MODULE,NGX_CORE_MODULE,NGX_CONF_MODULE,NGX_EVENT_MODULE,NGX_MAIL_MODULE
     */
    ngx_uint_t            type;              /* 模块类型 */
    
    /* 回调函数 */
    /* 主进程初始化的时候调用,但是目前为止框架代码从来没有调用过,因此可以设置为NULL */
    ngx_int_t           (*init_master)(ngx_log_t *log);         
    /* init_module回调方法在初始化所有模块时被调用。在master/worker模式下,这个阶段将在启动worker子进程前完成 */
    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);     
    /* init_precess回调方法在正常服务前被调用。在master/worker模式下,多个worker子进程已经产生,在每个worker进程的初始化过程中会调用所有模块的init_process函数*/
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);   
    /* 由于nginx暂不支持多线程模式,所以init_thread在框架代码中没有被调用过,设为NULL*/
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);     
    /* 同上,exit_thread也不支持,设为NULL*/
    void                (*exit_thread)(ngx_cycle_t *cycle);     
    /* exit_thread回调方法在服务停止前调用,在master/worker模式下,worker进程在退出前调用它*/
    void                (*exit_process)(ngx_cycle_t *cycle);   
    /*exit_master回调方法将在master进程退出前调用*/
    void                (*exit_master)(ngx_cycle_t *cycle);
    /**
     * 以下8个spare_hook变量是保留字段,目前没有使用,但可用nginx提供的 NGX_MODULE_V1_PADDING 宏来填充:
     * #define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0
    */
    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};

每个ngx_module_t都有各自的职责,都有各自的类型,基本分类如下:

NGX_CORE_MODULE
   |--ngx_core_module //核心模块之一,nginx启动加载的第一个模块,是nginx的核心模块之一;其主要用来保存不属于{}也不包括{}的全局配置项。
                      //nginx启动时在解析配置之前调用ngx_core_module_create_conf函数创建保存配置项的结构体,配置解析完以后调用ngx_core_module_init_conf函数处理配置文件中没有设置但是属于该模块负责的配置项。
   |--ngx_events_module //核心模块之一,它是其他所有事件模块的代理模块。nginx在启动时只与events模块打交道,而由events模块来加载其他事件模块;这样做的一个好处就是在添加新的事件模块处理新配置项时原有事件模块代码不需要做任何改动。
                        //events模块功能非常简单,它只负责处理events配置项(由ngx_events_block函数处理)。ngx_events_block函数做三件事:(1)为其他事件模块创建存储配置项结构指针的指针数组,并调用其他模块的create_conf函数;
						//(2)调用ngx_conf_parse函数解析events配置项内的配置项;(3)调用其他事件模块的init_conf函数
       NGX_EVENT_MODULE
	   |--ngx_event_core_module //第一个事件类型模块,它的主要功能是负责解析事件类配置项,选择事件处理机制(select,poll,epoll还是kequeue机制),创建连接池,预分配读写事件池等。
	                            //ngx_event_core_commands[] 中指定处理conf配置的关于sevents的配置项
       |--ngx_epoll_module
   |--ngx_http_module  //负责解析http{}中的配置,分配内存空间,并调用子模块create_main_conf(),create_srv_conf(),和create_loc_conf()来解析server{}和location{}中的配置。
       NGX_HTTP_MODULE
       |--ngx_http_core_module //解析http基本配置ngx_http_core_module
       |--ngx_http_access_module
       |--ngx_http_upstream_module
       |--ngx_http_auth_request_module
       |--ngx_http_autoindex_module
       |--ngx_http_browser_module
       |--ngx_http_charset_filter_module
       |--ngx_http_chunked_filter_module
       |--ngx_http_dav_module
       |--ngx_http_degradation_module
       |--ngx_http_empty_gif_module
       |--ngx_http_fastcgi_module
       |--ngx_http_flv_module
       |--ngx_http_geo_module
       |--ngx_http_geoip_module
       |--ngx_http_grpc_module
       |--ngx_http_gunzip_filter_module
       |--ngx_http_gzip_filter_module
       |--ngx_http_gzip_static_module
   |--ngx_stream_module
   |--ngx_google_perftools_module
   |--ngx_mail_module
   |--ngx_openssl_module
   |--ngx_thread_pool_module
   |--ngx_regex_module
   |--ngx_errlog_module

其中NGX_CORE_MODULE类型主要有三个:ngx_core_module 、ngx_events_module 、ngx_http_module,其他暂时不分析。主要的用途是解析nginx.conf中的配置,将配置保存到cycle->conf_ctx[]数组 中,并进行初始化。
NGX_CORE_MODULE类型的module主要的架构简化如下:

struct ngx_module_s {
    NGX_MODULE_V1,
    struct ngx_core_module_t,
    struct ngx_command_t,
    NGX_CORE_MODULE ,
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

其中struct ngx_core_module_t声明如下:

/**
 * 核心模块core数据结构
 * ngx_module_s->ctx 核心模块的上下文,主要定义了创建配置和初始化配置的结构
 */
typedef struct {
    ngx_str_t             name;
    void               *(*create_conf)(ngx_cycle_t *cycle);
    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;

struct ngx_command_t声明如下:

struct ngx_command_s {
    //配置项名称,如“gzip”
    ngx_str_t             name;
    //配置项类型,type将指定配置项可以出现的位置。例如,出现在server{}或location{}中,以及它可以携带的参数个数
    ngx_uint_t            type;
    //出现了name中指定的配置项后,将会调用set方法处理配置项的参数
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    //在配置文件中的偏移量
    ngx_uint_t            conf;
    //通常用于使用预设的解析方法解析配置项,这是配置模块的一个优秀设计,它与conf配合使用
    ngx_uint_t            offset;
    //配置项读取后的处理方法,必须是ngx_conf_post_t结构的指针
    void                 *post;
};

以ngx_core_module 为例:


ngx_module_t  ngx_core_module = {
    NGX_MODULE_V1,
    &ngx_core_module_ctx,                  /* module context */
    ngx_core_commands,                     /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

/**
 * 核心模块配置文件
 * ngx_core_module_create_conf 核心模块创建配置文件
 * ngx_core_module_init_conf 核心模块初始化配置文件
 */
static ngx_core_module_t  ngx_core_module_ctx = {
    ngx_string("core"),
    ngx_core_module_create_conf,
    ngx_core_module_init_conf
};

static ngx_command_t  ngx_core_commands[] = {

    { ngx_string("daemon"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      0,
      offsetof(ngx_core_conf_t, daemon),
      NULL },
     ......
      ngx_null_command
};

将ngx_core_module 展开:

ngx_module_t  ngx_core_module = {
    NGX_MODULE_V1,
    ngx_core_module_ctx = {
	    ngx_string("core"),
	    ngx_core_module_create_conf,
	    ngx_core_module_init_conf
	};                  /* module context */
    ngx_core_commands[] = {
	       { 
	          ngx_string("daemon"),
		      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
		      ngx_conf_set_flag_slot,
		      0,
		      offsetof(ngx_core_conf_t, daemon),
		      NULL
		   },
		   ......
		   ngx_null_command
	};                     /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};

整个modules解析和初始化的过程如下:

//在nginx主进程启动worker之前调用,用于初始化相关的配置
ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{
     .....
   /**
     * 模块创建和核心配置结构初始化ngx_core_conf_t
     *  (1)主要初始化cycle->modules
     *  (2)创建核心配置结构,Nginx的核心配置会放到ngx_core_conf_t  *ccf数据结构上
     *  (3)初始化核心配置结构ngx_core_conf_t  *
     */
    /* 创建模块以及创建模块的配置信息:cycle->modules[] = ngx_modules[]*/
    if (ngx_cycle_modules(cycle) != NGX_OK) {
        ngx_destroy_pool(pool);
        return NULL;
    }
      ......
    /*
     * 核心模块的配置文件创建
     * 配置创建调用nginx.c 中的 ngx_core_module_create_conf
     * 对类型为NGX_CORE_MODULE的模块调用对应的create_conf函数,进行申请空间之类的操作
     * */
    for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

        module = cycle->modules[i]->ctx; //模块上下文
        //为配置的数据结构分配内存空间,并完成默认初始化(为各个数据赋值-1)
        if (module->create_conf) {
            rv = module->create_conf(cycle); //模块回调函数,创建模块的配置信息,这里回调ngx_core_module->ngx_core_module_ctx->ngx_core_module_create_conf、
                                             //                                      ngx_event_core_module->ngx_event_core_module_ctx->ngx_event_core_create_conf
                                             //                                      ngx_events_module->ngx_events_module_ctx->NULL  create_conf为NULL
                                             //                                      ngx_http_module->ngx_http_module_ctx->NULL      create_conf为NULL                 
            if (rv == NULL) {
                ngx_destroy_pool(pool);
                return NULL;
            }
            cycle->conf_ctx[cycle->modules[i]->index] = rv; //配置文件复制; conf_ctx为cycle的void  ****conf_ctx数组
        }
    }
    ......
    /* 解析配置文件/usr/local/nginx/conf/nginx.conf 信息 */
    //根据上面给conf的赋值语句,ngx_conf_parse主要对配置文件NGX_MAIN_CONF这一层级的
    //配置进行解析,并只对NGX_CORE_MODULE类型的模块中的cmd进行匹配,然后执行匹配cmd对
    //应的函数
    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }
    ......
    //对类型为NGX_CORE_MODULE的模块调用对应的int_conf函数,对相关的conf结构进行初始化
    for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

        module = cycle->modules[i]->ctx;

        if (module->init_conf) {             //模块回调函数,创建模块的配置信息,这里回调ngx_core_module->ngx_core_module_ctx->ngx_core_module_init_conf、
                                             //                                      ngx_event_core_module->ngx_event_core_module_ctx->ngx_event_core_init_conf
                                             //                                      ngx_events_module->ngx_events_module_ctx->ngx_event_init_conf
                                             //                                      ngx_http_module->ngx_http_module_ctx->NULL      init_conf为NULL  
            if (module->init_conf(cycle,
                                  cycle->conf_ctx[cycle->modules[i]->index])
                == NGX_CONF_ERROR)
            {
                environ = senv;
                ngx_destroy_cycle_pools(&conf);
                return NULL;
            }
        }
    }
}

在ngx_init_cycle函数中,通过遍历module模块,当其类型为NGX_CORE_MODULE时,分别调用create_conf类型的函数为conf配置申请内存空间,调用init_conf类型的函数进行初始化。当然create_conf和init_conf都为函数指针,在不同的module模块中会被赋值成不同的函数进行回调。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值