nginx 源码学习笔记(十八)—— ngx_add_inherited_sockets 继承的sockets

来源:互联网 发布:mac air win8双系统 编辑:程序博客网 时间:2024/06/06 01:41

之前几节有讲过多进程的创建过程和子进程所处理的事情,今天要讲一下nginx里面main函数的另一个主要的操作ngx_add_inherited_sockets。

ngx_add_inherited_sockets:服务器监听套接字的封装。

本文的主要灵感来自:http://blog.csdn.net/livelylittlefish/article/details/7277607,感谢作者分享。

在ngx_add_inherited_sockets方法内,有一个重要的结构体需要讲解——ngx_listening_s

[cpp] view plaincopyprint?
  1. src/core/ngx_connection.h  
  2. typedef struct ngx_listening_s  ngx_listening_t;  
  3.   
  4. struct ngx_listening_s {  
  5.     ngx_socket_t        fd;                //文件描述符  
  6.   
  7.     struct sockaddr    *sockaddr;          //socket地址  
  8.     socklen_t           socklen;           //地址长度  
  9.     size_t              addr_text_max_len;   
  10.     ngx_str_t           addr_text;         //最终存放socket地址,之前的sockaddr主要存放没转换前的数据,之后会讲解  
  11.     int                 type;  
  12.   
  13.     int                 backlog;  
  14.     int                 rcvbuf;            //接受缓冲区大小  
  15.     int                 sndbuf;            //发送缓冲区大小  
  16.   
  17.     /* handler of accepted connection */  
  18.     ngx_connection_handler_pt   handler;  
  19.   
  20.     void               *servers;  /* array of ngx_http_in_addr_t, for example */  
  21.   
  22.     ngx_log_t           log;  
  23.     ngx_log_t          *logp;  
  24.   
  25.     size_t              pool_size;  
  26.     /* should be here because of the AcceptEx() preread */  
  27.     size_t              post_accept_buffer_size;  
  28.     /* should be here because of the deferred accept */  
  29.     ngx_msec_t          post_accept_timeout;  
  30.   
  31.     ngx_listening_t    *previous;  
  32.     ngx_connection_t   *connection;  
  33.   
  34.     unsigned            open:1;                 //下面的标志表示状态  
  35.     unsigned            remain:1;  
  36.     unsigned            ignore:1;  
  37.   
  38.     unsigned            bound:1;       /* already bound */  
  39.     unsigned            inherited:1;   /* inherited from previous process */  
  40.     unsigned            nonblocking_accept:1;  
  41.     unsigned            listen:1;  
  42.     unsigned            nonblocking:1;  
  43.     unsigned            shared:1;    /* shared between threads or processes */  
  44.     unsigned            addr_ntop:1;  
  45.   
  46. #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)  
  47.     unsigned            ipv6only:2;  
  48. #endif  
  49.   
  50. #if (NGX_HAVE_DEFERRED_ACCEPT)  
  51.     unsigned            deferred_accept:1;  
  52.     unsigned            delete_deferred:1;  
  53.     unsigned            add_deferred:1;  
  54. #ifdef SO_ACCEPTFILTER  
  55.     char               *accept_filter;  
  56. #endif  
  57. #endif  
  58. #if (NGX_HAVE_SETFIB)  
  59.     int                 setfib;  
  60. #endif  
  61.   
  62. };  


 

下面主要讲解下ngx_add_inherited_sockets:

[cpp] view plaincopyprint?
  1. src/core/nginx.c  
  2.   
  3. static ngx_int_t  
  4. ngx_add_inherited_sockets(ngx_cycle_t *cycle)  
  5. {  
  6.     u_char           *p, *v, *inherited;  
  7.     ngx_int_t         s;  
  8.     ngx_listening_t  *ls;  
  9.       
  10.     //获取环境变量 这里的"NGINX_VAR"是宏定义,值为"NGINX"  
  11.     inherited = (u_char *) getenv(NGINX_VAR);  
  12.   
  13.     if (inherited == NULL) {  
  14.         return NGX_OK;  
  15.     }  
  16.   
  17.     ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,  
  18.                   "using inherited sockets from \"%s\"", inherited);  
  19.       
  20.     //初始化ngx_cycle.listening数组,并且数组中包含10个元素  
  21.     if (ngx_array_init(&cycle->listening, cycle->pool, 10,  
  22.                        sizeof(ngx_listening_t))  
  23.         != NGX_OK)  
  24.     {  
  25.         return NGX_ERROR;  
  26.     }  
  27.       
  28.     //遍历环境变量  
  29.     for (p = inherited, v = p; *p; p++) {  
  30.       
  31.         //环境变量的值以':'or';'分开  
  32.         if (*p == ':' || *p == ';') {  
  33.             //转换十进制sockets  
  34.             s = ngx_atoi(v, p - v);  
  35.             if (s == NGX_ERROR) {  
  36.                 ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,  
  37.                               "invalid socket number \"%s\" in " NGINX_VAR  
  38.                               " environment variable, ignoring the rest"  
  39.                               " of the variable", v);  
  40.                 break;  
  41.             }  
  42.   
  43.             v = p + 1;  
  44.             //返回新分配的数组指针地址(在参考的blog里面这里解释可能有点错误)  
  45.             ls = ngx_array_push(&cycle->listening);  
  46.             if (ls == NULL) {  
  47.                 return NGX_ERROR;  
  48.             }  
  49.             //初始化内存空间  
  50.             ngx_memzero(ls, sizeof(ngx_listening_t));  
  51.             //保存socket文件描述符到数组中  
  52.             ls->fd = (ngx_socket_t) s;  
  53.         }  
  54.     }   
  55.   
  56.     ngx_inherited = 1; //表示已经的得到要继承的socket  
  57.       
  58.     //接下来详细讲解的函数  
  59.     return ngx_set_inherited_sockets(cycle);  
  60. }  
  61. /* 
  62. 根据上面的讲解,大致可以知道这个方法的用途: 
  63. 主要是读取环境变量"NGINX" 将其中各个用分隔符":"or";"的数值, 
  64. 保存在ngx_cycel->listening数组中 
  65. */  


 

下面介绍下:ngx_set_inherited_sockets

 

[cpp] view plaincopyprint?
  1. src/core/ngx_connection.c  
  2.   
  3. ngx_int_t  
  4. ngx_set_inherited_sockets(ngx_cycle_t *cycle)  
  5. {  
  6.     size_t                     len;  
  7.     ngx_uint_t                 i;  
  8.     ngx_listening_t           *ls;  
  9.     socklen_t                  olen;  
  10. #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)  
  11.     ngx_err_t                  err;  
  12.     struct accept_filter_arg   af;  
  13. #endif  
  14. #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)  
  15.     int                        timeout;  
  16. #endif  
  17.     //取出cycle->listening数组中的数据地址  
  18.     ls = cycle->listening.elts;  
  19.     //遍历数组  
  20.     //要记得之前讲过数组当中存放的是ngx_listening_t结构体  
  21.     for (i = 0; i < cycle->listening.nelts; i++) {  
  22.         //ls的fd已经在之前赋值了  
  23.         //sockaddr分配内存空间  
  24.         ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN);  
  25.         if (ls[i].sockaddr == NULL) {  
  26.             return NGX_ERROR;  
  27.         }  
  28.            
  29.         ls[i].socklen = NGX_SOCKADDRLEN;  
  30.         //获取socket名字,要用于判断是否有效  
  31.         if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) {  
  32.             ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,  
  33.                           "getsockname() of the inherited "  
  34.                           "socket #%d failed", ls[i].fd);  
  35.             ls[i].ignore = 1;  
  36.             continue;  
  37.         }  
  38.         //查看sockaddr 地址族类型 根据类型设置最大长度  
  39.         switch (ls[i].sockaddr->sa_family) {  
  40.   
  41. #if (NGX_HAVE_INET6)  
  42.         case AF_INET6:  
  43.              ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN;  
  44.              len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1;  
  45.              break;  
  46. #endif  
  47.   
  48. #if (NGX_HAVE_UNIX_DOMAIN)  
  49.         case AF_UNIX:  
  50.              ls[i].addr_text_max_len = NGX_UNIX_ADDRSTRLEN;  
  51.              len = NGX_UNIX_ADDRSTRLEN;  
  52.              break;  
  53. #endif  
  54.   
  55.         case AF_INET:  
  56.              ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;  
  57.              len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;  
  58.              break;  
  59.   
  60.         default:  
  61.             ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,  
  62.                           "the inherited socket #%d has "  
  63.                           "an unsupported protocol family", ls[i].fd);  
  64.             ls[i].ignore = 1;  
  65.             continue;  
  66.         }  
  67.   
  68.         ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len);  
  69.         if (ls[i].addr_text.data == NULL) {  
  70.             return NGX_ERROR;  
  71.         }  
  72.         //之前的长度主要为了下面的转换做准备  
  73.         //将socket绑定的地址转换为文本格式(ipv4和ipv6的不相同)  
  74.         len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, len, 1);  
  75.         if (len == 0) {  
  76.             return NGX_ERROR;  
  77.         }  
  78.   
  79.         ls[i].addr_text.len = len;  
  80.         //这里设置类每个监听的socket的backlog为511  
  81.         ls[i].backlog = NGX_LISTEN_BACKLOG;  
  82.   
  83.         olen = sizeof(int);  
  84.         //获取文件描述符的接受缓冲区大小,并用rcvbuf保存,并且指定rcvbuf大小olen  
  85.         if (getsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (void *) &ls[i].rcvbuf,  
  86.                        &olen)  
  87.             == -1)  
  88.         {  
  89.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,  
  90.                           "getsockopt(SO_RCVBUF) %V failed, ignored",  
  91.                           &ls[i].addr_text);  
  92.   
  93.             ls[i].rcvbuf = -1;  
  94.         }  
  95.   
  96.         olen = sizeof(int);  
  97.         //获取文件描述符发送缓冲区大小,并用sndbuf保存,并且指定sndbuf大小olen  
  98.         if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (void *) &ls[i].sndbuf,  
  99.                        &olen)  
  100.             == -1)  
  101.         {  
  102.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,  
  103.                           "getsockopt(SO_SNDBUF) %V failed, ignored",  
  104.                           &ls[i].addr_text);  
  105.   
  106.             ls[i].sndbuf = -1;  
  107.         }  
  108.   
  109. #if 0  
  110.         /* SO_SETFIB is currently a set only option */  
  111.   
  112. #if (NGX_HAVE_SETFIB)  
  113.   
  114.         if (getsockopt(ls[i].setfib, SOL_SOCKET, SO_SETFIB,  
  115.                        (void *) &ls[i].setfib, &olen)  
  116.             == -1)  
  117.         {  
  118.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,  
  119.                           "getsockopt(SO_SETFIB) %V failed, ignored",  
  120.                           &ls[i].addr_text);  
  121.   
  122.             ls[i].setfib = -1;  
  123.         }  
  124.   
  125. #endif  
  126. #endif  
  127. /* 
  128. 当支持accept filter时,通过SO_ACCEPTFILTER选项取得socket的accept_filter表 
  129. 保存在对应项的accept_filter中; 
  130. 下面是SO_ACCEPTFILTER的解释(因为我的书里没有所以上网找的) 
  131.  
  132. SO_ACCEPTFILTER 是socket上的输入过滤,他在接手前 
  133. 将过滤掉传入流套接字的链接,功能是服务器不等待 
  134. 最后的ACK包而仅仅等待携带数据负载的包 
  135.  
  136. */  
  137. #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)  
  138.   
  139.         ngx_memzero(&af, sizeof(struct accept_filter_arg));  
  140.         olen = sizeof(struct accept_filter_arg);  
  141.   
  142.         if (getsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, &olen)  
  143.             == -1)  
  144.         {  
  145.             err = ngx_errno;  
  146.   
  147.             if (err == NGX_EINVAL) {  
  148.                 continue;  
  149.             }  
  150.   
  151.             ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,  
  152.                           "getsockopt(SO_ACCEPTFILTER) for %V failed, ignored",  
  153.                           &ls[i].addr_text);  
  154.             continue;  
  155.         }  
  156.   
  157.         if (olen < sizeof(struct accept_filter_arg) || af.af_name[0] == '\0') {  
  158.             continue;  
  159.         }  
  160.   
  161.         ls[i].accept_filter = ngx_palloc(cycle->pool, 16);  
  162.         if (ls[i].accept_filter == NULL) {  
  163.             return NGX_ERROR;  
  164.         }  
  165.   
  166.         (void) ngx_cpystrn((u_char *) ls[i].accept_filter,  
  167.                            (u_char *) af.af_name, 16);  
  168. #endif  
  169. /* 
  170. 如果当前操作系统TCP层支持TCP_DEFER_ACCEPT, 
  171. 则试图获取TCP_DEFER_ACCEPT的timeout值。Timeout大于0时, 
  172. 则将socket对应deferred_accept标志设为1 
  173. 详细解释卸写在录里面了哦!!! 
  174. */  
  175.   
  176.   
  177. #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)  
  178.   
  179.         timeout = 0;  
  180.         olen = sizeof(int);  
  181.   
  182.         if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &olen)  
  183.             == -1)  
  184.         {  
  185.             ngx_log_error(NGX_LOG_NOTICE, cycle->log, ngx_errno,  
  186.                           "getsockopt(TCP_DEFER_ACCEPT) for %V failed, ignored",  
  187.                           &ls[i].addr_text);  
  188.             continue;  
  189.         }  
  190.   
  191.         if (olen < sizeof(int) || timeout == 0) {  
  192.             continue;  
  193.         }  
  194.   
  195.         ls[i].deferred_accept = 1;  
  196. #endif  
  197.     }  
  198.   
  199.     return NGX_OK;  
  200. }  


总结:

可以看出

ngx_add_inherited_sockets:主要是通过环境变量,获取到fd的值,然后存在数组当中;

ngx_set_inherited_sockets:主要是对数组中的每一个元素进行判断是否有效,然后进行初始化操作。

 

附录:

TCP_DEFER_ACCEPT
我 们首先考虑的第1个选项是TCP_DEFER_ACCEPT(这是Linux系统上的叫法,其他一些操作系统上也有同样的选项但使用不同的名字)。为了理 解TCP_DEFER_ACCEPT选项的具体思想,我们有必要大致阐述一下典型的HTTP客户/服务器交互过程。请回想下TCP是如何与传输数据的目标建立连接的。在网络上,在分离的单元之间传输的信息称为IP包(或IP 数据报)。一个包总有一个携带服务信息的包头,包头用于内部协议的处理,并且它也可以携带数据负载。服务信息的典型例子就是一套所谓的标志,它把包标记代表TCP/IP协议栈内的特殊含义,例如收到包的成功确认等等。通常,在经过“标记”的包里携带负载是完全可能的,但有时,内部逻辑迫使TCP/IP协议 栈发出只有包头的IP包。这些包经常会引发讨厌的网络延迟而且还增加了系统的负载,结果导致网络性能在整体上降低。


现在服务器创建了一个套接字同时等待连接。TCP/IP式的连接过程就是所谓“3次握手”。首先,客户程序发送一个设置SYN标志而且不带数据负载的TCP包(一个SYN包)。服务器则以发出带SYN/ACK标志的数据包(一个SYN/ACK包)作为刚才收到包的确认响应。客户随后发送一个ACK包确认收到了第2个包从而结束连接 过程。在收到客户发来的这个SYN/ACK包之后,服务器会唤醒一个接收进程等待数据到达。当3次握手完成后,客户程序即开始把“有用的”的数据发送给服务器。通常,一个HTTP请求的量是很小的而且完全可以装到一个包里。但是,在以上的情况下,至少有4个包将用来进行双向传输,这样就增加了可观的延迟时间。此外,你还得注意到,在“有用的”数据被发送之前,接收方已经开始在等待信息了。


为了减轻这些问题所带来的影响,Linux(以及其他的 一些操作系统)在其TCP实现中包括了TCP_DEFER_ACCEPT选项。它们设置在侦听套接字的服务器方,该选项命令内核不等待最后的ACK包而且在第1个真正有数据的包到达才初始化侦听进程。在发送SYN/ACK包之后,服务器就会等待客户程序发送含数据的IP包。现在,只需要在网络上传送3个包 了,而且还显著降低了连接建立的延迟,对HTTP通信而言尤其如此。

 

对于那些支持deffered accept的操作系统,nginx会设置这个参数来增强功能,设置了这个参数,在accept的时候,只有当实际收到了数据,才唤醒在accept等待的进程,可以减少一些无聊的上下文切换,如下:

0 0
原创粉丝点击