天天看點

nginx源碼分析—處理繼承的sockets

作者:阿波

連結:http://blog.csdn.net/livelylittlefish/article/details/7277607

Content

0.序

1.偵聽結構

2. ngx_add_inherited_sockets()分析

3. ngx_set_inherited_sockets()分析

4.小結

0.序

本文将着重分析初始化過程中如何處理繼承的sockets。文中如無特别說明,.表示nginx-1.0.4代碼目錄,本文為/usr/src/nginx-1.0.4。

1.偵聽結構

偵聽結構較為複雜,描述如下。

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;     /* 發送緩沖區 */

    /* 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;

    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:2;
#endif

#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

};
           

sizeof(ngx_listening_t)=184。結構如下圖。

nginx源碼分析—處理繼承的sockets

ngx_listening_t與ngx_connection_t之間的關系如下圖。

nginx源碼分析—處理繼承的sockets

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_t
ngx_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);  /* NGINX_VAR為宏,值為"NGINX" */

    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 == ';') {  /* sockets以':'或者';'隔開 */
            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;  /* 儲存該socket至listening數組元素的fd字段 */
        }
    }

    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 0x6b02a0
0x6b02a0:       16000
(gdb) x/w 0x6b0358
0x6b0358:       16500
(gdb) x/w 0x6b0410
0x6b0410:       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_len
  • ls[i].addr_text
  • ls[i].backlog
  • ls[i].rcvbuf (調用getsockopt())
  • ls[i].sndbuf (調用getsockopt())
  • ls[i].accept_filter
  • ls[i].deferred_accept

涉及到的相關系統函數調用如下。

  • getenv()擷取環境變量,并傳回指向該值字元串的指針;
  • getsockname()擷取socket名字(位址及長度);
  • getsockopt()擷取socket選項;

具體請參考附錄或者源代碼。

4.小結

本文主要分析初始化過程中如何處理繼承的sockets。

Reference

# man getenv

# mangetsockopt

# man getsockname

繼續閱讀