zoukankan      html  css  js  c++  java
  • Nginx源码研究八:nginx监听socket实现流程

    前面描述了nginx系统分析nginx的配置文件,初始化模块相关参数的过程,这里利用nginx监听socket的实现过程,做一次完整的回顾

    1、首先,nginx启动的main函数中,会先初始化cycle数据结构

        cycle = ngx_init_cycle(&init_cycle);

    2、在初始化cycle中,nginx做了关于生成配置参数项,分析配置文件,初始化配置参数项等工作。当然在完成这些工作后,nginx还在初始化cycle函数中,完成了监听socket的流程

      
       //生成NGX_CORE_MODULE模块的核心配置项
       for (i = 0; ngx_modules[i]; i++) {
            if (ngx_modules[i]->type != NGX_CORE_MODULE) {
                continue;
            }
    
            module = ngx_modules[i]->ctx;
    
            if (module->create_conf) {
                rv = module->create_conf(cycle);
                if (rv == NULL) {
                    ngx_destroy_pool(pool);
                    return NULL;
                }
                cycle->conf_ctx[ngx_modules[i]->index] = rv;
            }
        }
    
    
        senv = environ;
    
        ngx_memzero(&conf, sizeof(ngx_conf_t));
        conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t));
        if (conf.args == NULL) {
            ngx_destroy_pool(pool);
            return NULL;
        }
    
        conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
        if (conf.temp_pool == NULL) {
            ngx_destroy_pool(pool);
            return NULL;
        }
    
    
        conf.ctx = cycle->conf_ctx;
        conf.cycle = cycle;
        conf.pool = pool;
        conf.log = log;
        conf.module_type = NGX_CORE_MODULE;
        conf.cmd_type = NGX_MAIN_CONF; 
    
    #if 0
        log->log_level = NGX_LOG_DEBUG_ALL;
    #endif
    
    
        if (ngx_conf_param(&conf) != NGX_CONF_OK) {
            environ = senv;
            ngx_destroy_cycle_pools(&conf);
            return NULL;
        }
    
        //分析配置文件 
        if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
            environ = senv;
            ngx_destroy_cycle_pools(&conf);
            return NULL;
        }
    
        if (ngx_test_config && !ngx_quiet_mode) {
            ngx_log_stderr(0, "the configuration file %s syntax is ok",
                           cycle->conf_file.data);
        }
    
    
        //初始化NGX_CORE_MODULE类型模块
        for (i = 0; ngx_modules[i]; i++) {
            if (ngx_modules[i]->type != NGX_CORE_MODULE) {
                continue;
            }
    
            module = ngx_modules[i]->ctx;
    
            if (module->init_conf) {
                if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index])
                    == NGX_CONF_ERROR)
                {
                    environ = senv;
                    ngx_destroy_cycle_pools(&conf);
                    return NULL;
                }
            }
        }

    3、在分析配置文件中,符合下面的流程,首先配置文件格式有下面几种

      a、格式1       

    X a b;

         

         b、格式2

    X{
    }

      在分析中X,a,b都会被推进cf->args数组中; 然后分析,所有模块类型是NGX_CORE_MODULE,指令类型包含NGX_MAIN_CONF这种类型的,同时指令的名称和X的名称一致的指令,如果有,则利用这个指令去做参数的设置。 我们看格式2的指令情况,例如X是http,配置文件的分析中,遇到“http {” 时候,会执行 ngx_http_block命令

    static ngx_command_t  ngx_http_commands[] = {
    
        { ngx_string("http"),
          NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
          ngx_http_block,
          0,
          0,
          NULL },
    
          ngx_null_command
    };
    
    
    static ngx_core_module_t  ngx_http_module_ctx = {
        ngx_string("http"),
        NULL,
        NULL
    };
    
    
    ngx_module_t  ngx_http_module = {
        NGX_MODULE_V1,
        &ngx_http_module_ctx,                  /* module context */
        ngx_http_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
    };

    4、执行ngx_http_block命令,我们看代码可以知道, ngx_http_module模块会对所有http模块做管理,对于将配置信息分成,main_conf, srv_conf和 loc_conf,我们在前面已经绘制图形表示过,这里不再重复,同时在ngx_http_block命令中,会继续分析配置文件(并非重头开始,而是从"http{" 后面开始)

        /* parse inside the http{} block */
    
        cf->module_type = NGX_HTTP_MODULE;
        cf->cmd_type = NGX_HTTP_MAIN_CONF;
        rv = ngx_conf_parse(cf, NULL);

    5、分析的配置信息是http{}内部的参数信息,我们可以知道,http{}内部的参数格式如下

    http {
        include       mime.types;
        
        server {
        }

        # another virtual host using mix of IP-, name-, and port-based configuration
        #
        #server {
        #    listen       8000;
        #    listen       somename:8080;
        #    server_name  somename  alias  another.alias;

        #    location / {
        #        root   html;
        #        index  index.html index.htm;
        #    }
        #}

    }

    6、我们知道,listen在nginx中用来描述定义的web服务绑定的端口,配置的时候都是放在 server{}中,到这个时候,我们已经知道nginx在分析配置信息中发现“server {”的处理流程了,在NGX_HTTP_MODULE类型模块中,找到指令类型是NGX_HTTP_MAIN_CONF,指令名称是server的指令去执行。我们在ngx_http_core_module中找到了server指令

        { ngx_string("server"),
          NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
          ngx_http_core_server,
          0,
          0,
          NULL },

    7、分析server参数的项使用的是同样的办法,这里不再细述。

        /* parse inside server{} */
    
        pcf = *cf;
        cf->ctx = ctx;
        cf->cmd_type = NGX_HTTP_SRV_CONF;
    
        rv = ngx_conf_parse(cf, NULL);

    8、到此,我们基本知道,nginx关于listen参数的定制的实现,是在在NGX_HTTP_MODULE类型模块中,找到指令类型是NGX_HTTP_SRV_CONF,指令名称是listen的指令去执行。 我们在ngx_http_core_module中找到符合要求的指令。

        { ngx_string("listen"),
          NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
          ngx_http_core_listen,
          NGX_HTTP_SRV_CONF_OFFSET,
          0,
          NULL },

     

    9、listen指令的ngx_http_core_listen工作是去设置用户设置的值。

    static char *
    ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
        ngx_http_core_srv_conf_t *cscf = conf;
    
        ngx_str_t              *value, size;
        ngx_url_t               u;
        ngx_uint_t              n;
        ngx_http_listen_opt_t   lsopt;
    
        cscf->listen = 1;
    
        value = cf->args->elts;
    
        ngx_memzero(&u, sizeof(ngx_url_t));
    
        u.url = value[1];
        u.listen = 1;
        u.default_port = 80;
    
        //listen可以用url的格式 例如 listen somename:8080;下面是实现过程
        if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
            if (u.err) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "%s in "%V" of the "listen" directive",
                                   u.err, &u.url);
            }
    
            return NGX_CONF_ERROR;
        }
    
        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
    
        ngx_memcpy(&lsopt.u.sockaddr, u.sockaddr, u.socklen);
    
        lsopt.socklen = u.socklen;
        lsopt.backlog = NGX_LISTEN_BACKLOG;
        lsopt.rcvbuf = -1;
        lsopt.sndbuf = -1;
    #if (NGX_HAVE_SETFIB)
        lsopt.setfib = -1;
    #endif
        lsopt.wildcard = u.wildcard;
    #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
        lsopt.ipv6only = 1;
    #endif
    
        (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr,
                             NGX_SOCKADDR_STRLEN, 1);
    
        for (n = 2; n < cf->args->nelts; n++) {
    
       }
    
        if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {
            return NGX_CONF_OK;
        }
    
        return NGX_CONF_ERROR;

    10、我们可以看见,多个server中,用户定义的端口会被放置在cmcf->ports数组中

    ngx_int_t
    ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
        ngx_http_listen_opt_t *lsopt)
    {
        in_port_t                   p;
        ngx_uint_t                  i;
        struct sockaddr            *sa;
        struct sockaddr_in         *sin;
        ngx_http_conf_port_t       *port;
        ngx_http_core_main_conf_t  *cmcf;
    #if (NGX_HAVE_INET6)
        struct sockaddr_in6        *sin6;
    #endif
    
        cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
    
        ......
    
        port = ngx_array_push(cmcf->ports);
        if (port == NULL) {
            return NGX_ERROR;
        }
    
        port->family = sa->sa_family;
        port->port = p;
        port->addrs.elts = NULL;
    
        return ngx_http_add_address(cf, cscf, port, lsopt);
    }

    11、继续第3步开始的ngx_http_block分析,在ngx_http_optimize_servers方法中,我们可以看到,建立了从配置文件分析listen的信息(存储到cmcf->ports结构中),会关联到cycle->listening数组中

     /* optimize the lists of ports, addresses and server names */
    
        if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    static ngx_int_t
    ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
    {
        ngx_uint_t                 i, last, bind_wildcard;
        ngx_listening_t           *ls;
        ngx_http_port_t           *hport;
        ngx_http_conf_addr_t      *addr;
    
        addr = port->addrs.elts;
        last = port->addrs.nelts;
    
        /*
         * If there is a binding to an "*:port" then we need to bind() to
         * the "*:port" only and ignore other implicit bindings.  The bindings
         * have been already sorted: explicit bindings are on the start, then
         * implicit bindings go, and wildcard binding is in the end.
         */
    
        if (addr[last - 1].opt.wildcard) {
            addr[last - 1].opt.bind = 1;
            bind_wildcard = 1;
    
        } else {
            bind_wildcard = 0;
        }
    
        i = 0;
    
        while (i < last) {
    
            if (bind_wildcard && !addr[i].opt.bind) {
                i++;
                continue;
            }
    
            ls = ngx_http_add_listening(cf, &addr[i]);
            if (ls == NULL) {
                return NGX_ERROR;
            }
           ……
        }
        ……
    }

    12、完成用户定制的分析过程后,在初始化化cycle中,nginx打开了监听的socket

        if (ngx_open_listening_sockets(cycle) != NGX_OK) {
            goto failed;
        }

     

  • 相关阅读:
    request对象
    js基础3
    Andorid Binder进程间通信---总结
    java 获取系统变量(环境变量和设置变量)
    參加项目管理培训的一些体会
    select poll使用
    关注关注工作行列
    jquery——zTree, 完美好用的树插件
    OSI七层模型具体解释
    (原创)优酷androidclient 下载中 bug 解决
  • 原文地址:https://www.cnblogs.com/yimuren/p/4485161.html
Copyright © 2011-2022 走看看