博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nginx源码研究八:nginx监听socket实现流程
阅读量:5161 次
发布时间:2019-06-13

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

前面描述了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_tngx_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_tngx_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;    }

 

 

 

 

转载于:https://www.cnblogs.com/yimuren/p/4485161.html

你可能感兴趣的文章
一周TDD小结
查看>>
sizeof与strlen的用法
查看>>
Linux 下常见目录及其功能
查看>>
开源框架中常用的php函数
查看>>
nginx 的提升多个小文件访问的性能模块
查看>>
set&map
查看>>
集合类总结
查看>>
4.AE中的缩放,书签
查看>>
CVE-2014-6321 && MS14-066 Microsoft Schannel Remote Code Execution Vulnerability Analysis
查看>>
给一次重新选择的机会_您还会选择程序员吗?
查看>>
Mysql MHA高可用集群架构
查看>>
心急的C小加
查看>>
编译原理 First,Follow,select集求法
查看>>
(一一二)图文混排中特殊文字的点击与事件处理
查看>>
iPhone开发经典语录集锦 (转)
查看>>
SVM基础必备常识
查看>>
FPGA时序约束的几种方法 (转)
查看>>
cocos2dx 3.x tolua 分析
查看>>
oracle 外网访问
查看>>
jdbc连接数据库方式问题
查看>>