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
- src/core/ngx_connection.h
- typedef struct ngx_listening_s ngx_listening_t;
- struct ngx_listening_s {
- ngx_socket_t fd; //文件描述符
- struct sockaddr *sockaddr; //socket地址
- socklen_t socklen; //地址长度
- size_t addr_text_max_len;
- ngx_str_t addr_text; //最终存放socket地址,之前的sockaddr主要存放没转换前的数据,之后会讲解
- 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_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
- };
下面主要讲解下ngx_add_inherited_sockets:
- src/core/nginx.c
- 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;
- //获取环境变量 这里的"NGINX_VAR"是宏定义,值为"NGINX"
- 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);
- //初始化ngx_cycle.listening数组,并且数组中包含10个元素
- if (ngx_array_init(&cycle->listening, cycle->pool, 10,
- sizeof(ngx_listening_t))
- != NGX_OK)
- {
- return NGX_ERROR;
- }
- //遍历环境变量
- for (p = inherited, v = p; *p; p++) {
- //环境变量的值以':'or';'分开
- if (*p == ':' || *p == ';') {
- //转换十进制sockets
- s = ngx_atoi(v, p - v);
- 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 + 1;
- //返回新分配的数组指针地址(在参考的blog里面这里解释可能有点错误)
- ls = ngx_array_push(&cycle->listening);
- if (ls == NULL) {
- return NGX_ERROR;
- }
- //初始化内存空间
- ngx_memzero(ls, sizeof(ngx_listening_t));
- //保存socket文件描述符到数组中
- ls->fd = (ngx_socket_t) s;
- }
- }
- ngx_inherited = 1; //表示已经的得到要继承的socket
- //接下来详细讲解的函数
- return ngx_set_inherited_sockets(cycle);
- }
- /*
- 根据上面的讲解,大致可以知道这个方法的用途:
- 主要是读取环境变量"NGINX" 将其中各个用分隔符":"or";"的数值,
- 保存在ngx_cycel->listening数组中
- */
下面介绍下:ngx_set_inherited_sockets
- src/core/ngx_connection.c
- ngx_int_t
- ngx_set_inherited_sockets(ngx_cycle_t *cycle)
- {
- size_t len;
- ngx_uint_t i;
- ngx_listening_t *ls;
- socklen_t olen;
- #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
- ngx_err_t err;
- struct accept_filter_arg af;
- #endif
- #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
- int timeout;
- #endif
- //取出cycle->listening数组中的数据地址
- ls = cycle->listening.elts;
- //遍历数组
- //要记得之前讲过数组当中存放的是ngx_listening_t结构体
- for (i = 0; i < cycle->listening.nelts; i++) {
- //ls的fd已经在之前赋值了
- //sockaddr分配内存空间
- ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN);
- if (ls[i].sockaddr == NULL) {
- return NGX_ERROR;
- }
- ls[i].socklen = NGX_SOCKADDRLEN;
- //获取socket名字,要用于判断是否有效
- if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) {
- ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
- "getsockname() of the inherited "
- "socket #%d failed", ls[i].fd);
- ls[i].ignore = 1;
- continue;
- }
- //查看sockaddr 地址族类型 根据类型设置最大长度
- switch (ls[i].sockaddr->sa_family) {
- #if (NGX_HAVE_INET6)
- case AF_INET6:
- ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN;
- len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1;
- break;
- #endif
- #if (NGX_HAVE_UNIX_DOMAIN)
- case AF_UNIX:
- ls[i].addr_text_max_len = NGX_UNIX_ADDRSTRLEN;
- len = NGX_UNIX_ADDRSTRLEN;
- break;
- #endif
- case AF_INET:
- ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;
- len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
- break;
- default:
- ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
- "the inherited socket #%d has "
- "an unsupported protocol family", ls[i].fd);
- ls[i].ignore = 1;
- continue;
- }
- ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len);
- if (ls[i].addr_text.data == NULL) {
- return NGX_ERROR;
- }
- //之前的长度主要为了下面的转换做准备
- //将socket绑定的地址转换为文本格式(ipv4和ipv6的不相同)
- len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, len, 1);
- if (len == 0) {
- return NGX_ERROR;
- }
- ls[i].addr_text.len = len;
- //这里设置类每个监听的socket的backlog为511
- ls[i].backlog = NGX_LISTEN_BACKLOG;
- olen = sizeof(int);
- //获取文件描述符的接受缓冲区大小,并用rcvbuf保存,并且指定rcvbuf大小olen
- if (getsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (void *) &ls[i].rcvbuf,
- &olen)
- == -1)
- {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
- "getsockopt(SO_RCVBUF) %V failed, ignored",
- &ls[i].addr_text);
- ls[i].rcvbuf = -1;
- }
- olen = sizeof(int);
- //获取文件描述符发送缓冲区大小,并用sndbuf保存,并且指定sndbuf大小olen
- if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (void *) &ls[i].sndbuf,
- &olen)
- == -1)
- {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
- "getsockopt(SO_SNDBUF) %V failed, ignored",
- &ls[i].addr_text);
- ls[i].sndbuf = -1;
- }
- #if 0
- /* SO_SETFIB is currently a set only option */
- #if (NGX_HAVE_SETFIB)
- if (getsockopt(ls[i].setfib, SOL_SOCKET, SO_SETFIB,
- (void *) &ls[i].setfib, &olen)
- == -1)
- {
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
- "getsockopt(SO_SETFIB) %V failed, ignored",
- &ls[i].addr_text);
- ls[i].setfib = -1;
- }
- #endif
- #endif
- /*
- 当支持accept filter时,通过SO_ACCEPTFILTER选项取得socket的accept_filter表
- 保存在对应项的accept_filter中;
- 下面是SO_ACCEPTFILTER的解释(因为我的书里没有所以上网找的)
- SO_ACCEPTFILTER 是socket上的输入过滤,他在接手前
- 将过滤掉传入流套接字的链接,功能是服务器不等待
- 最后的ACK包而仅仅等待携带数据负载的包
- */
- #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
- ngx_memzero(&af, sizeof(struct accept_filter_arg));
- olen = sizeof(struct accept_filter_arg);
- if (getsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, &olen)
- == -1)
- {
- err = ngx_errno;
- if (err == NGX_EINVAL) {
- continue;
- }
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,
- "getsockopt(SO_ACCEPTFILTER) for %V failed, ignored",
- &ls[i].addr_text);
- continue;
- }
- if (olen < sizeof(struct accept_filter_arg) || af.af_name[0] == '\0') {
- continue;
- }
- ls[i].accept_filter = ngx_palloc(cycle->pool, 16);
- if (ls[i].accept_filter == NULL) {
- return NGX_ERROR;
- }
- (void) ngx_cpystrn((u_char *) ls[i].accept_filter,
- (u_char *) af.af_name, 16);
- #endif
- /*
- 如果当前操作系统TCP层支持TCP_DEFER_ACCEPT,
- 则试图获取TCP_DEFER_ACCEPT的timeout值。Timeout大于0时,
- 则将socket对应deferred_accept标志设为1
- 详细解释卸写在录里面了哦!!!
- */
- #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
- timeout = 0;
- olen = sizeof(int);
- if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &olen)
- == -1)
- {
- ngx_log_error(NGX_LOG_NOTICE, cycle->log, ngx_errno,
- "getsockopt(TCP_DEFER_ACCEPT) for %V failed, ignored",
- &ls[i].addr_text);
- continue;
- }
- if (olen < sizeof(int) || timeout == 0) {
- continue;
- }
- ls[i].deferred_accept = 1;
- #endif
- }
- return NGX_OK;
- }
总结:
可以看出
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等待的进程,可以减少一些无聊的上下文切换,如下:
- nginx 源码学习笔记(十八)—— ngx_add_inherited_sockets 继承的sockets
- nginx 源码学习笔记(十八)—— ngx_add_inherited_sockets 继承的sockets
- nginx源码分析—处理继承的sockets
- nginx源码分析—处理继承的sockets
- nginx源码分析—处理继承的sockets
- nginx源码分析—处理继承的sockets
- 析构—Swift学习笔记(十八)
- nginx 源码学习笔记(十四)—— 全局变量ngx_cycle
- nginx 源码学习笔记(十四)—— 全局变量ngx_cycle
- Nginx学习笔记(十八):事件处理框架
- 学习笔记(十八)
- nginx 源码学习笔记(一)——初识nginx helloworld模块
- nginx 源码学习笔记(二)——nginx精粹-模块
- nginx 源码学习笔记(三)——nginx精粹-模块
- nginx 源码学习笔记(四)——nginx精粹-模块
- nginx 源码学习笔记(五)——nginx精粹-模块
- nginx 源码学习笔记(六)——nginx基本数据结构
- nginx 源码学习笔记(十九)—— nginx启动过程函数调用图
- Eclipse中编写servlet时出现"The import javax.servlet cannot be resolved" 问题解决办法
- httpclient基础
- iOS开发——CF前缀
- 通过偏移将GPS坐标转换成百度坐标--js实现
- Dell PowerEdge R710服务器Centos6.2在线扩容raid
- nginx 源码学习笔记(十八)—— ngx_add_inherited_sockets 继承的sockets
- The string "--" is not permitted within comments.
- 指针浅析
- Day 1: Bower —— 管理你的客户端依赖关系
- apache-tomcat-7.0.63 下部署solr-5.3.1 并创建core
- 物联网概念思考
- JAVA LOG系统介绍
- Modbus CRC16校验算法--查表法(已经过本人测试,工作良好)
- 常见多线程与并发服务器设计方案举例