ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv) { char **env, *var; u_char *p; ngx_uint_t i, n; ngx_pid_t pid; ngx_exec_ctx_t ctx; ngx_core_conf_t *ccf; ngx_listening_t *ls; ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t)); ctx.path = argv[0]; ctx.name = "new binary process"; ctx.argv = argv; n = 2; env = ngx_set_environment(cycle, &n); if (env == NULL) { return NGX_INVALID_PID; } var = ngx_alloc(sizeof(NGINX_VAR) + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2, cycle->log); if (var == NULL) { ngx_free(env); return NGX_INVALID_PID; } p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR)); ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { p = ngx_sprintf(p, "%ud;", ls[i].fd); } *p = '\0'; env[n++] = var; #if (NGX_SETPROCTITLE_USES_ENV) /* allocate the spare 300 bytes for the new binary process title */ env[n++] = "SPARE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; #endif env[n] = NULL; #if (NGX_DEBUG) { char **e; for (e = env; *e; e++) { ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, "env: %s", *e); } } #endif ctx.envp = (char *const *) env; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_rename_file_n " %s to %s failed " "before executing new binary process \"%s\"", ccf->pid.data, ccf->oldpid.data, argv[0]); ngx_free(env); ngx_free(var); return NGX_INVALID_PID; } pid = ngx_execute(cycle, &ctx); if (pid == NGX_INVALID_PID) { if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_rename_file_n " %s back to %s failed after " "an attempt to execute new binary process \"%s\"", ccf->oldpid.data, ccf->pid.data, argv[0]); } } ngx_free(env); ngx_free(var); return pid; }
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數組中 */
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; }
能夠看出html
ngx_add_inherited_sockets:主要是經過環境變量,獲取到fd的值,而後存在數組當中;nginx
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個包將用來進行雙向傳輸,這樣就增長了可觀的延遲時間。此外,你還得注意到,在「有用的」數據被髮送以前,接收方已經開始在等待信息了。socket
爲了減輕這些問題所帶來的影響,Linux(以及其餘的 一些操做系統)在其TCP實現中包括了TCP_DEFER_ACCEPT選項。它們設置在偵聽套接字的服務器方,該選項命令內核不等待最後的ACK包並且在第1個真正有數據的包到達才初始化偵聽進程。在發送SYN/ACK包以後,服務器就會等待客戶程序發送含數據的IP包。如今,只須要在網絡上傳送3個包 了,並且還顯著下降了鏈接創建的延遲,對HTTP通訊而言尤爲如此。函數
對於那些支持deffered accept的操做系統,nginx會設置這個參數來加強功能,設置了這個參數,在accept的時候,只有當實際收到了數據,才喚醒在accept等待的進程,能夠減小一些無聊的上下文切換。性能
後半部分轉自:ui
http://www.cnblogs.com/h2-database/archive/2012/05/23/2583263.htmlspa