nginx 源码学习笔记(十八)—— ngx_add_inherited_sockets 继承的sockets
之前幾節(jié)有講過多進(jìn)程的創(chuàng)建過程和子進(jìn)程所處理的事情,今天要講一下nginx里面main函數(shù)的另一個主要的操作ngx_add_inherited_sockets。
ngx_add_inherited_sockets:服務(wù)器監(jiān)聽套接字的封裝。
本文的主要靈感來自:http://blog.csdn.net/livelylittlefish/article/details/7277607,感謝作者分享。
在ngx_add_inherited_sockets方法內(nèi),有一個重要的結(jié)構(gòu)體需要講解——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主要存放沒轉(zhuǎn)換前的數(shù)據(jù),之后會講解 int type; int backlog; int rcvbuf; //接受緩沖區(qū)大小 int sndbuf; //發(fā)送緩沖區(qū)大小 /* 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; //下面的標(biāo)志表示狀態(tài) 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; //獲取環(huán)境變量 這里的"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數(shù)組,并且數(shù)組中包含10個元素 if (ngx_array_init(&cycle->listening, cycle->pool, 10, sizeof(ngx_listening_t)) != NGX_OK) { return NGX_ERROR; } //遍歷環(huán)境變量 for (p = inherited, v = p; *p; p++) { //環(huán)境變量的值以':'or';'分開 if (*p == ':' || *p == ';') { //轉(zhuǎn)換十進(jìn)制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; //返回新分配的數(shù)組指針地址(在參考的blog里面這里解釋可能有點錯誤) ls = ngx_array_push(&cycle->listening); if (ls == NULL) { return NGX_ERROR; } //初始化內(nèi)存空間 ngx_memzero(ls, sizeof(ngx_listening_t)); //保存socket文件描述符到數(shù)組中 ls->fd = (ngx_socket_t) s; } } ngx_inherited = 1; //表示已經(jīng)的得到要繼承的socket //接下來詳細(xì)講解的函數(shù) return ngx_set_inherited_sockets(cycle); } /* 根據(jù)上面的講解,大致可以知道這個方法的用途: 主要是讀取環(huán)境變量"NGINX" 將其中各個用分隔符":"or";"的數(shù)值, 保存在ngx_cycel->listening數(shù)組中 */
?
下面介紹下: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數(shù)組中的數(shù)據(jù)地址 ls = cycle->listening.elts; //遍歷數(shù)組 //要記得之前講過數(shù)組當(dāng)中存放的是ngx_listening_t結(jié)構(gòu)體 for (i = 0; i < cycle->listening.nelts; i++) { //ls的fd已經(jīng)在之前賦值了 //sockaddr分配內(nèi)存空間 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 地址族類型 根據(jù)類型設(shè)置最大長度 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; } //之前的長度主要為了下面的轉(zhuǎn)換做準(zhǔn)備 //將socket綁定的地址轉(zhuǎn)換為文本格式(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; //這里設(shè)置類每個監(jiān)聽的socket的backlog為511 ls[i].backlog = NGX_LISTEN_BACKLOG; olen = sizeof(int); //獲取文件描述符的接受緩沖區(qū)大小,并用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); //獲取文件描述符發(fā)送緩沖區(qū)大小,并用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 /* 當(dāng)支持accept filter時,通過SO_ACCEPTFILTER選項取得socket的accept_filter表 保存在對應(yīng)項的accept_filter中; 下面是SO_ACCEPTFILTER的解釋(因為我的書里沒有所以上網(wǎng)找的) SO_ACCEPTFILTER 是socket上的輸入過濾,他在接手前 將過濾掉傳入流套接字的鏈接,功能是服務(wù)器不等待 最后的ACK包而僅僅等待攜帶數(shù)據(jù)負(fù)載的包 */ #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 /* 如果當(dāng)前操作系統(tǒng)TCP層支持TCP_DEFER_ACCEPT, 則試圖獲取TCP_DEFER_ACCEPT的timeout值。Timeout大于0時, 則將socket對應(yīng)deferred_accept標(biāo)志設(shè)為1 詳細(xì)解釋卸寫在錄里面了哦!!! */ #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; }總結(jié):
可以看出
ngx_add_inherited_sockets:主要是通過環(huán)境變量,獲取到fd的值,然后存在數(shù)組當(dāng)中;
ngx_set_inherited_sockets:主要是對數(shù)組中的每一個元素進(jìn)行判斷是否有效,然后進(jìn)行初始化操作。
?
附錄:
TCP_DEFER_ACCEPT
我 們首先考慮的第1個選項是TCP_DEFER_ACCEPT(這是Linux系統(tǒng)上的叫法,其他一些操作系統(tǒng)上也有同樣的選項但使用不同的名字)。為了理 解TCP_DEFER_ACCEPT選項的具體思想,我們有必要大致闡述一下典型的HTTP客戶/服務(wù)器交互過程。請回想下TCP是如何與傳輸數(shù)據(jù)的目標(biāo)建立連接的。在網(wǎng)絡(luò)上,在分離的單元之間傳輸?shù)男畔⒎Q為IP包(或IP 數(shù)據(jù)報)。一個包總有一個攜帶服務(wù)信息的包頭,包頭用于內(nèi)部協(xié)議的處理,并且它也可以攜帶數(shù)據(jù)負(fù)載。服務(wù)信息的典型例子就是一套所謂的標(biāo)志,它把包標(biāo)記代表TCP/IP協(xié)議棧內(nèi)的特殊含義,例如收到包的成功確認(rèn)等等。通常,在經(jīng)過“標(biāo)記”的包里攜帶負(fù)載是完全可能的,但有時,內(nèi)部邏輯迫使TCP/IP協(xié)議 棧發(fā)出只有包頭的IP包。這些包經(jīng)常會引發(fā)討厭的網(wǎng)絡(luò)延遲而且還增加了系統(tǒng)的負(fù)載,結(jié)果導(dǎo)致網(wǎng)絡(luò)性能在整體上降低。
現(xiàn)在服務(wù)器創(chuàng)建了一個套接字同時等待連接。TCP/IP式的連接過程就是所謂“3次握手”。首先,客戶程序發(fā)送一個設(shè)置SYN標(biāo)志而且不帶數(shù)據(jù)負(fù)載的TCP包(一個SYN包)。服務(wù)器則以發(fā)出帶SYN/ACK標(biāo)志的數(shù)據(jù)包(一個SYN/ACK包)作為剛才收到包的確認(rèn)響應(yīng)。客戶隨后發(fā)送一個ACK包確認(rèn)收到了第2個包從而結(jié)束連接 過程。在收到客戶發(fā)來的這個SYN/ACK包之后,服務(wù)器會喚醒一個接收進(jìn)程等待數(shù)據(jù)到達(dá)。當(dāng)3次握手完成后,客戶程序即開始把“有用的”的數(shù)據(jù)發(fā)送給服務(wù)器。通常,一個HTTP請求的量是很小的而且完全可以裝到一個包里。但是,在以上的情況下,至少有4個包將用來進(jìn)行雙向傳輸,這樣就增加了可觀的延遲時間。此外,你還得注意到,在“有用的”數(shù)據(jù)被發(fā)送之前,接收方已經(jīng)開始在等待信息了。
為了減輕這些問題所帶來的影響,Linux(以及其他的 一些操作系統(tǒng))在其TCP實現(xiàn)中包括了TCP_DEFER_ACCEPT選項。它們設(shè)置在偵聽套接字的服務(wù)器方,該選項命令內(nèi)核不等待最后的ACK包而且在第1個真正有數(shù)據(jù)的包到達(dá)才初始化偵聽進(jìn)程。在發(fā)送SYN/ACK包之后,服務(wù)器就會等待客戶程序發(fā)送含數(shù)據(jù)的IP包。現(xiàn)在,只需要在網(wǎng)絡(luò)上傳送3個包 了,而且還顯著降低了連接建立的延遲,對HTTP通信而言尤其如此。
?
對于那些支持deffered accept的操作系統(tǒng),nginx會設(shè)置這個參數(shù)來增強(qiáng)功能,設(shè)置了這個參數(shù),在accept的時候,只有當(dāng)實際收到了數(shù)據(jù),才喚醒在accept等待的進(jìn)程,可以減少一些無聊的上下文切換,如下:
?
?
總結(jié)
以上是生活随笔為你收集整理的nginx 源码学习笔记(十八)—— ngx_add_inherited_sockets 继承的sockets的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用 jQuery 获取 iframe 父
- 下一篇: 让理科生沉默,让文科生落泪的文史综合题