nginx源码分析—处理继承的sockets

来源:互联网 发布:淘宝服务站加盟 编辑:程序博客网 时间:2024/05/18 15:25

Content

  1. 侦听结构
  2. ngx_add_inherited_sockets()分析
  3. ngx_set_inherited_sockets()分析
  4. 小结

1.序

源代码:

//通过环境变量NGINX完成socket的继承,继承来的socket将会放到init_cycle的listening数组中。//在NGINX环境变量中,每个socket中间用冒号或分号隔开。完成继承同时设置全局变量ngx_inherited为1    if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {        return 1;    }

2.侦听结构

侦听结构较为复杂,描述如下。

file: ./src/core/ngx_connection.h

typedef struct ngx_listening_s  ngx_listening_t;struct ngx_listening_s {    ngx_socket_t        fd;/*?文件描述符,即socket?*/    struct sockaddr    *sockaddr;/*?socket地址?*/    socklen_t           socklen;    /* size of sockaddr */    size_t              addr_text_max_len;    ngx_str_t           addr_text;    int                 type;    int                 backlog;    int                 rcvbuf;/*?接收缓冲区?*/    int                 sndbuf;/*?发送缓冲区?*/#if (NGX_HAVE_KEEPALIVE_TUNABLE)    int                 keepidle;    int                 keepintvl;    int                 keepcnt;#endif    /* handler of accepted connection */    ngx_connection_handler_pt   handler;    void               *servers;  /* array of ngx_http_in_addr_t, for example */    ngx_log_t           log;    ngx_log_t          *logp;    size_t              pool_size;    /* should be here because of the AcceptEx() preread */    size_t              post_accept_buffer_size;    /* should be here because of the deferred accept */    ngx_msec_t          post_accept_timeout;    ngx_listening_t    *previous;/*?指向前一个ngx_listening_t结构?*/    ngx_connection_t   *connection;    ngx_uint_t          worker;    unsigned            open:1;    unsigned            remain:1;    unsigned            ignore:1;    unsigned            bound:1;       /* already bound */    unsigned            inherited:1;   /* inherited from previous process */    unsigned            nonblocking_accept:1;    unsigned            listen:1;    unsigned            nonblocking:1;    unsigned            shared:1;    /* shared between threads or processes */    unsigned            addr_ntop:1;#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)    unsigned            ipv6only:1;#endif#if (NGX_HAVE_REUSEPORT)    unsigned            reuseport:1;    unsigned            add_reuseport:1;#endif    unsigned            keepalive:2;#if (NGX_HAVE_DEFERRED_ACCEPT)    unsigned            deferred_accept:1;    unsigned            delete_deferred:1;    unsigned            add_deferred:1;#ifdef SO_ACCEPTFILTER    char               *accept_filter;#endif#endif#if (NGX_HAVE_SETFIB)    int                 setfib;#endif#if (NGX_HAVE_TCP_FASTOPEN)    int                 fastopen;#endif};

sizeof(ngx_listening_t)=184。结构如下图。

这里写图片描述

ngx_listening_t与ngx_connection_t之间的关系如下图。

这里写图片描述

2. ngx_add_inherited_sockets()分析

该函数通过解析环境变量NGINX_VAR=”NGINX”,将其中的 socket number 保存至 ngx_cycle.listening 数组,该数组元素类型为ngx_listening_t。这些socekts在环境变量中以’:’或’;’隔开。

例如,为调试方便,设环境变量NGINX为如下值

export NGINX=”16000:16500:16600;”

注意:当然,后续的测试并不一定会成功,此处只是为了分析该函数。例如,可能会出现如下错误。
nginx: [crit] getsockname() of the inherited socket #16000 failed (9: Bad file descriptor)
nginx: [crit] getsockname() of the inherited socket #16500 failed (9: Bad file descriptor)
nginx: [crit] getsockname() of the inherited socket #16600 failed (9: Bad file descriptor)

/*?传入该参数的是init_cycle,调用ngx_init_cycle()后全局变量ngx_cycle会指向该结构*/static ngx_int_tngx_add_inherited_sockets(ngx_cycle_t *cycle){    u_char           *p, *v, *inherited;    ngx_int_t         s;    ngx_listening_t  *ls;    inherited = (u_char *) getenv(NGINX_VAR);//从环境中取字符串,获取环境变量的值    if (inherited == NULL) {        return NGX_OK;    }    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,                  "using inherited sockets from \"%s\"", inherited);    if (ngx_array_init(&cycle->listening, cycle->pool, 10,                       sizeof(ngx_listening_t))        != NGX_OK)/*?初始化ngx_cycle.listening数组,?10个元素空间?*/    {        return NGX_ERROR;    }    for (p = inherited, v = p; *p; p++) {        if (*p == ':' || *p == ';') {            s = ngx_atoi(v, p - v);/*?sockets是10进制正整数?*/            if (s == NGX_ERROR) {                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,                              "invalid socket number \"%s\" in " NGINX_VAR                              " environment variable, ignoring the rest"                              " of the variable", v);                break;            }            /*第一次满足该条件时v和p分别指向如下值,此时解析出s=16000            (gdb) p v             $6 = (u_char *) 0x6b0139 "16000:16500:16600;"            (gdb) p p             $7 = (u_char *) 0x6b013e ":16500:16600;"            */            v = p + 1;            ls = ngx_array_push(&cycle->listening);/*?将合法的socket?number加入该数组*/            if (ls == NULL) {                return NGX_ERROR;            }            ngx_memzero(ls, sizeof(ngx_listening_t));            ls->fd = (ngx_socket_t) s;        }    }    ngx_inherited = 1;    return ngx_set_inherited_sockets(cycle);/*?该函数逐一设置cycle->listening数组每个元素?*/}

调试过程中,得到以下信息,供参考。

(gdb) p cycle->listening$11 = {elts = 0x6b02a0, nelts = 3, size = 184, nalloc = 10, pool = 0x6af650}(gdb) x/w 0x6b02a00x6b02a0:       16000(gdb) x/w 0x6b03580x6b0358:       16500(gdb) x/w 0x6b04100x6b0410:       16600

可以验证,0x6b0358-0x6b02a0=0xB8=184,0x6b0410-0x6b0358=0xB8=184。

3. ngx_set_inherited_sockets()分析

该函数从参数cycle(后续调用ngx_init_cycle()函数后全局变量ngx_cycle会指向该参数)的listening数组中逐一对每个元素(ngx_listening_t结构)进行初始化,即初始化除fd字段外的其他的字段。

因此,ngx_set_inherited_sockets()函数主要完成以下事情。

对全局变量ngx_cycle的listening数组,逐一设置该数组每个元素的以下字段

ls[i].sockaddr (调用getsockname())ls[i].addr_text_max_lenls[i].addr_textls[i].backlogls[i].rcvbuf (调用getsockopt())ls[i].sndbuf (调用getsockopt())ls[i].accept_filterls[i].deferred_accept

涉及到的相关系统函数调用如下。

getenv()获取环境变量,并返回指向该值字符串的指针;getsockname()获取socket名字(地址及长度)getsockopt()获取socket选项;
0 0
原创粉丝点击