亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
123下一頁
最近訪問板塊 發(fā)新帖
查看: 21598 | 回復(fù): 22
打印 上一主題 下一主題

原碼分析第三期<<Nginx源代碼情景分析(1)——預(yù)備知識>> [復(fù)制鏈接]

論壇徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2011-11-22 21:45 |只看該作者 |倒序瀏覽
本帖最后由 duanjigang 于 2011-11-22 23:02 編輯

Nginx源代碼情景分析

dreamice <jiangjunyong@gmail.com>
2011-11-21



第1章 預(yù)備知識
1.1 Nginx簡介
Nginx(Engine X)是一款高性能的HTTP、反向代理與負(fù)載均衡均衡服務(wù)器,同時也支持IMAP/POP3/SMPT代理服務(wù)器。Nginx的原作者是俄羅斯人Igor,他將源代碼以類BSD許可證的形式發(fā)布。Nginx發(fā)布以來,因為其穩(wěn)定性,豐富的功能集成,示例配置文件和低系統(tǒng)資源消耗而聞名。目前國內(nèi)各大門戶網(wǎng)站及相關(guān)應(yīng)用越來越多的部署了Nginx,如sina,網(wǎng)易,淘寶,騰訊等。因此,越來越多WEB服務(wù)器應(yīng)用開發(fā)者、WEB高性能研究者以及WEB應(yīng)用安全研究者都投入了對Nginx的研究。甚至很多人開始語言因為Nginx的高性能、低資源消耗級穩(wěn)定性的優(yōu)勢,有逐步搶奪Apache對WEB服務(wù)器市場占有量的趨勢。
下圖是各大WEB服務(wù)器自1995至2011年的市場占有率情況:

圖 1 各大WEB服務(wù)器市場占有率情況

1.2 必備基礎(chǔ)知識
由于Nginx的代碼主要由C語言,部分嵌入式匯編及腳本語言組成。因此,分析Nginx源代碼,必須具備扎實的C語言基礎(chǔ),最好能能夠讀懂嵌入式匯編語言,對shell腳本語言也需要有一定的掌握。
作為一個高性能的WEB服務(wù)器,分析Nginx的源代碼,你還需要具備HTTP協(xié)議的基礎(chǔ)知識,另外需要掌握一定的網(wǎng)絡(luò)知識,尤其是socket編程,對TCP/IP協(xié)議有一定的了解。本情景分析主要是分析Linux的版本,因此,你還需要掌握Linux編程的相關(guān)知識(強(qiáng)烈推薦閱讀《UNIX環(huán)境高級編程》一書)。另外,最好對系統(tǒng)及軟件程序架構(gòu)有一定的了解,這樣可以更深入的理解Nginx的模塊化構(gòu)成。
1.3 Nginx的目錄結(jié)構(gòu)
本情景分析以當(dāng)前Nginx的最新版本1.1.7為參考,其他版本其主要的原理和核心代碼差異不大。
以下是Nginx源代碼的目錄樹結(jié)構(gòu):

圖 2 Nginx源碼目錄樹

Src為核心源代碼目錄,其中主要的幾個目錄及包含的源代碼簡介如下:
core : 該目錄存放核心基礎(chǔ)模塊的代碼,也是Nginx服務(wù)的入口
http : HTTP協(xié)議處理模塊的代碼,Nginx作為WEB服務(wù)器和代理服務(wù)器運(yùn)行時的核心模塊
mail : Mail處理模塊的代碼,Nginx作為pop3/imap/smtp代理服務(wù)器運(yùn)行時的核心模塊
event : Nginx 自身對事件處理邏輯的封裝
os : Nginx對各個平臺抽象邏輯的封裝
misc : nginx 的一些utils,定義了test和profiler的一些外圍模塊的邏輯

其他幾個目錄:
auto:系統(tǒng)執(zhí)行./configure時,所依賴的一些自動化腳本。
conf:Nginx相關(guān)運(yùn)行時調(diào)用的配置文件模版
objs:編譯生成的目標(biāo)文件存放目錄
man:幫助文檔
html:默認(rèn)的訪問文件存放目錄
1.4 基礎(chǔ)數(shù)據(jù)類型
在Nginx源碼中,定義了很多數(shù)據(jù)類型,對原始的基礎(chǔ)數(shù)據(jù)類型進(jìn)行了封裝。其中,一些基礎(chǔ)的數(shù)據(jù)類型,在平時的編程中很少用到,這里特別說明一下。
在 core/ngx_config.h 目錄里面定義了以下幾個基本的數(shù)據(jù)類型的映射:
typedef intptr_t        ngx_int_t;
typedef uintptr_t       ngx_uint_t;
typedef intptr_t        ngx_flag_t;
在linux系統(tǒng)中,這幾個數(shù)據(jù)類型在/usr/include/stdint.h的定義為:
/* Types for `void *' pointers.  */
#if __WORDSIZE == 64
# ifndef __intptr_t_defined
typedef long int                intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned long int       uintptr_t;
#else
# ifndef __intptr_t_defined
typedef int                     intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned int            uintptr_t;
#endif
其他的數(shù)據(jù)類型,均以以上幾個基礎(chǔ)數(shù)據(jù)類型為參考,進(jìn)行其他復(fù)雜的數(shù)據(jù)結(jié)構(gòu)的封裝定義。
由于Nginx在編程時,很多地方都自己封裝了一套數(shù)據(jù)結(jié)構(gòu),帶有自身的特色,這里就不一一進(jìn)行說明,在后續(xù)的情景分析中,再將逐步進(jìn)行深入分析。
第2章 Nginx啟動與執(zhí)行流程
2.1 Nginx啟動
“工欲善其事必先利其器”。毛主席說,理論實踐相結(jié)合,分析研究源代碼,也不例外。我們首先搭建一個Nginx的環(huán)境,以源碼編譯安裝,然后從配置深入了解配置文件,啟動參數(shù),最后結(jié)合代碼中熟悉的main()函數(shù)開始,走向一條合理的分析線路。Nginx的安裝在這里就不贅述。
我們進(jìn)入Nginx安裝好的目錄,可以看到如下圖所示的目錄樹結(jié)構(gòu):

圖 3 Nginx安裝完成后的目錄結(jié)構(gòu)樹
如上圖所示,最重要的是nginx.conf這個文件,里面包含了nginx配置的各項參數(shù)。我們在這里先不深入分析其內(nèi)容,而從啟動開始。默認(rèn)安裝后,不需要進(jìn)行配置也可以按默認(rèn)項啟動。
執(zhí)行“nginx -?”看看幫助,我們發(fā)現(xiàn)Nginx有以下啟動選項。
root@debian:/nginx# ./sbin/nginx -?
nginx: nginx version: nginx/1.0.5
nginx: Usage: nginx [-?hvVtq] [-s signal] [-c filename] [-p prefix] [-g directives]

Options:
  -?,-h         : this help
  -v            : show version and exit
  -V            : show version and configure options then exit
  -t            : test configuration and exit
  -q            : suppress non-error messages during configuration testing
  -s signal     : send signal to a master process: stop, quit, reopen, reload
  -p prefix     : set prefix path (default: /nginx/)
  -c filename   : set configuration file (default: conf/nginx.conf)
  -g directives : set global directives out of configuration file

默認(rèn)情況下,在nginx安裝后的目錄執(zhí)行./sbin/nginx即可啟動,默認(rèn)端口是80,通過瀏覽器,我們可以實現(xiàn)對nginx web服務(wù)器的訪問。
2.2 Nginx執(zhí)行流程
和普通的應(yīng)用程序一樣,Nginx程序的執(zhí)行入口函數(shù)仍舊從main()函數(shù)開始。Main()函數(shù)位于src/core/nginx.c中。Nginx啟動執(zhí)行主要調(diào)用的函數(shù)概況如下:
main()
  --ngx_debug_init()
  --ngx_strerror_init()
  --ngx_get_options(argc, argv)
  --ngx_time_init()
  --ngx_regex_init()
  --ngx_log_init()
  --ngx_ssl_init()
  --ngx_memzero()
  --ngx_create_pool()
  --ngx_save_argv()
  --ngx_process_options()
  --ngx_os_init()
  --ngx_crc32_table_init()
  --ngx_add_inherited_sockets()
  --ngx_init_cycle()
  --ngx_signal_process()
  --ngx_os_status()
  --ngx_get_conf()
  --ngx_init_signals()
  --ngx_daemon()
  --ngx_create_pidfile()
  ----ngx_single_process_cycle()
  ----ngx_master_process_cycle()
以上是main函數(shù)調(diào)用到函數(shù),其中可能包括一些分支才能調(diào)用到的函數(shù),也在這里一并順序羅列。我們現(xiàn)對這個調(diào)用流程有一個整體的印象,然后在深入代碼,分析每個函數(shù),并逐步分析完這個流程執(zhí)行過程所做的事情。
下面我們開始正式的源代碼情景分析:
198 int ngx_cdecl
199 main(int argc, char *const *argv)
200 {
201     ngx_int_t         i;
202     ngx_log_t        *log;
203     ngx_cycle_t      *cycle, init_cycle;
204     ngx_core_conf_t  *ccf;
205
206     ngx_debug_init();
207
208     if (ngx_strerror_init() != NGX_OK) {
209         return 1;
210     }
211
212     if (ngx_get_options(argc, argv) != NGX_OK) {
213         return 1;
214     }
以上是main函數(shù)啟動最開始的執(zhí)行片段,具體的數(shù)據(jù)類型我們到函數(shù)中進(jìn)行分析,因此,main執(zhí)行開始涉及到的變量類型暫不進(jìn)行分析。第一個函數(shù)ngx_debug_init(),這個函數(shù)主要是作為初始化debug用的,nginx中的debug,主要是對內(nèi)存池分配管理方面的debug,因為作為一個應(yīng)用程序,最容易出現(xiàn)bug的地方也是內(nèi)存管理這塊。在Linux版本中,這個函數(shù)只是一個空定義(src/os/unix/ngx_linux_config.h)。
#define ngx_debug_init()
2.2.1 ngx_strerror_init函數(shù)分析
我們來到程序的第208行,ngx_strerror_init(),該函數(shù)的定義在文件src/os/unix/ngx_errno.c中。該函數(shù)主要初始化系統(tǒng)中錯誤編號對應(yīng)的含義,這樣初始化中進(jìn)行對應(yīng)的好處是,當(dāng)出現(xiàn)錯誤,不用再去調(diào)用strerror()函數(shù)來獲取錯誤原因,而直接可以根據(jù)錯誤編號找到對應(yīng)的錯誤原因,可以提高運(yùn)行時的執(zhí)行效率。從這里可以看到,nginx開發(fā)者的別有用心,對于微小的性能提升也毫不放過。
44 ngx_uint_t
45 ngx_strerror_init(void)
46 {
47     char       *msg;
48     u_char     *p;
49     size_t      len;
50     ngx_err_t   err;
51
52     /*
53      * ngx_strerror() is not ready to work at this stage, therefore,
54      * malloc() is used and possible errors are logged using strerror().
55      */
56
57     len = NGX_SYS_NERR * sizeof(ngx_str_t);  
58
59     ngx_sys_errlist = malloc(len);
60     if (ngx_sys_errlist == NULL) {
61         goto failed;
62     }
63
64     for (err = 0; err < NGX_SYS_NERR; err++) {
65         msg = strerror(err);
66         len = ngx_strlen(msg);
67
68         p = malloc(len);
69         if (p == NULL) {
70             goto failed;
71         }
72
73         ngx_memcpy(p, msg, len);
74         ngx_sys_errlist[err].len = len;
75         ngx_sys_errlist[err].data = p;
76     }
77
78     return NGX_OK;
79
80 failed:
81
82     err = errno;
83     ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err));
84
85     return NGX_ERROR;
86 }
第57行中,NGX_SYS_NERR定義在objs/ngx_auto_config.h文件中,特別注意,這是一個auto性質(zhì)的文件,只有在源碼安裝nginx時,執(zhí)行了./configure后,才能生成這個文件。
171 #ifndef NGX_SYS_NERR
172 #define NGX_SYS_NERR  132
173 #endif
在Linux系統(tǒng)中有132個錯誤編碼。我們可以寫一個簡單的小程序來測試系統(tǒng)中的錯誤編碼對應(yīng)的說明:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>


#define ERRO_NUM        132

int main()
{
        int i;
        for(i = 0; i < ERRO_NUM; i++)
                printf("%d:%s\n", i, strerror(i));
        return 0;
}
具體執(zhí)行結(jié)果請讀者運(yùn)行查看,在這里不再贅述。

同樣在第57行中,sizeof(ngx_str_t)這是對nginx自己定義的字符串結(jié)構(gòu)的字節(jié)長度計算。我們來分析一下nginx的字符串表示形式。
在src/core/ngx_string.h文件中:
15 typedef struct {
16     size_t      len;
17     u_char     *data;
18 } ngx_str_t;
19
20
21 typedef struct {
22     ngx_str_t   key;
23     ngx_str_t   value;
24 } ngx_keyval_t;
Nginx自身對字符串的表示,進(jìn)行了“長度—內(nèi)容”這樣的方式,字符串在初始化時就進(jìn)行了長度的計算記錄,這樣就方便了后續(xù)在使用過程中,不必重復(fù)計算字符串長度。同樣是很細(xì)微的提升效率的表現(xiàn)啊!
接著回到對ngx_strerror_init的分析,第59行,malloc為全局的ngx_sys_errlist分配內(nèi)存,這是一個全局靜態(tài)變量,指向錯誤編碼及對應(yīng)字符串說明值數(shù)組的起始地址。從64至76行,完成了數(shù)組中,每一個值對應(yīng)的err字符串及其長度的初始化工作。這部分也是該函數(shù)的核心工作部分。在這里nginx并沒有使用其內(nèi)存池,而是使用率默認(rèn)的malloc進(jìn)行內(nèi)存分配,因為在這里程序還沒有創(chuàng)建內(nèi)存池,而后續(xù)的初始化工作,可能出現(xiàn)未知的錯誤,那么,該處初始化的錯誤數(shù)組就可以派上用場了。

1.jpg (64 KB, 下載次數(shù): )

1.jpg

2.jpg (28.26 KB, 下載次數(shù): )

2.jpg

3.jpg (35.7 KB, 下載次數(shù): )

3.jpg

論壇徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
2 [報告]
發(fā)表于 2011-11-22 21:47 |只看該作者
本帖最后由 dreamice 于 2011-11-23 20:49 編輯

2.2.2 ngx_get_options()
ngx_get_options函數(shù)主要是解析Nginx的啟動參數(shù),Nginx的啟動選項在2.1節(jié)中已經(jīng)說明。
657 static ngx_int_t
658 ngx_get_options(int argc, char *const *argv)
659 {
660     u_char     *p;
661     ngx_int_t   i;
662
/* 這個地方特別注意,argc是從1開始檢查解析的,我們知道nginx的默認(rèn)啟動,可以不帶任何參數(shù),所以,如果是默認(rèn)第一次啟動nginx,就不用進(jìn)入for循環(huán),而直接返回 */
663     for (i = 1; i < argc; i++) {
664
665         p = (u_char *) argv;
666
/* Nginx的啟動選項,只要帶了參數(shù)的,都以“-”開始,因此此處主要檢查參數(shù)的合法性,如果不合法,就不在做后續(xù)檢查,直接返回。這個地方指針也用得比較巧妙,*p++檢查當(dāng)前p指向的字符,然后指針下移。*/
667         if (*p++ != '-') {
668             ngx_log_stderr(0, "invalid option: \"%s\"", argv);
669             return NGX_ERROR;
670         }
671
672         while (*p) {
673
674             switch (*p++) {
675
/* 如果是-?或者-h,則既要顯示版本信息,又要顯示幫助 */
676             case '?':
677             case 'h':
678                 ngx_show_version = 1;
679                 ngx_show_help = 1;
680                 break;
681
/* 如果是執(zhí)行 –v,則顯示版本信息 */
682             case 'v':
683                 ngx_show_version = 1;
684                 break;
685
/* 如果執(zhí)行-V,則顯示版本信息,并顯示相關(guān)配置信息,主要包括編譯時的gcc版本,啟用了哪些編譯選項等 */
686             case 'V':
687                 ngx_show_version = 1;
688                 ngx_show_configure = 1;
689                 break;
690
/* 如果執(zhí)行的是-t,則用于test nginx的配置是否有語法錯誤,如果有錯誤則會提示,沒有錯誤會提示syntax ok和successful 字樣,這個跟apache類似。*/
691             case 't':
692                 ngx_test_config = 1;
693                 break;
694
/* -q是quiet模式,主要是在配置測試過程中,避免非錯誤信息的輸出 */
695             case 'q':
696                 ngx_quiet_mode = 1;
697                 break;
698
/* -p主要是指明nginx啟動時的配置目錄,這對于重新配置nginx目錄時有用 */
699             case 'p':
700                 if (*p) {
701                     ngx_prefix = p;
702                     goto next;
703                 }
704
705                 if (argv[++i]) {
706                     ngx_prefix = (u_char *) argv;
707                     goto next;
708                 }
709
710                 ngx_log_stderr(0, "option \"-p\" requires directory name");
711                 return NGX_ERROR;
712
/* -c指明啟動配置文件nginx.conf的路徑,當(dāng)該文件存儲在非標(biāo)準(zhǔn)目錄的時候有用 */
713             case 'c':
714                 if (*p) {
715                     ngx_conf_file = p;
716                     goto next;
717                 }
718
719                 if (argv[++i]) {
720                     ngx_conf_file = (u_char *) argv;
721                     goto next;
722                 }
723
724                 ngx_log_stderr(0, "option \"-c\" requires file name");
725                 return NGX_ERROR;
726
/* -g 指明設(shè)置配置文件的全局指令,如:nginx -g "pid /var/run/nginx.pid; worker_processes `sysctl -n hw.ncpu`;",多個選項之間以分號分開 */
727             case 'g':
728                 if (*p) {
729                     ngx_conf_params = p;
730                     goto next;
731                 }
732
733                 if (argv[++i]) {
734                     ngx_conf_params = (u_char *) argv;
735                     goto next;
736                 }
737
738                 ngx_log_stderr(0, "option \"-g\" requires parameter");
739                 return NGX_ERROR;
740
/* -s是信號處理選項,主要可以處理stop, quit, reopen, reload 這幾個作用的信號,其中,stop為停止運(yùn)行,quit為退出,reopen為重新打開,reload為重新讀配置文件。信號都是有master進(jìn)程處理的,關(guān)于master和worker進(jìn)程,在后續(xù)章節(jié)中介紹 */
741             case 's':       
/* 接下來的if-else主要是將全局變量ngx_signal指向信號值,以方便在后續(xù)的處理中,來判斷處理具體的信號 */
742                 if (*p) {
743                     ngx_signal = (char *) p;
744
745                 } else if (argv[++i]) {
746                     ngx_signal = argv;
747
748                 } else {
749                     ngx_log_stderr(0, "option \"-s\" requires parameter");
750                     return NGX_ERROR;
751                 }
752
753                 if (ngx_strcmp(ngx_signal, "stop") == 0
754                     || ngx_strcmp(ngx_signal, "quit") == 0
755                     || ngx_strcmp(ngx_signal, "reopen") == 0
756                     || ngx_strcmp(ngx_signal, "reload") == 0)
757                 {
758                     ngx_process = NGX_PROCESS_SIGNALLER;
759                     goto next;
760                 }
761
762                 ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);
763                 return NGX_ERROR;
764
765             default:
766                 ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));
767                 return NGX_ERROR;
768             }
769         }
770
771     next:
772
773         continue;
774     }
775
776     return NGX_OK;
777 }
ngx_get_options函數(shù)體之所以是一個while循環(huán),是因為nginx可以一次傳遞多個參數(shù),比如“nginx –V –c file”,所以只有循環(huán)才能解析完整個啟動命令參數(shù)值。

論壇徽章:
0
3 [報告]
發(fā)表于 2011-11-22 23:04 |只看該作者
支持!

論壇徽章:
0
4 [報告]
發(fā)表于 2011-11-23 00:06 |只看該作者
占位

論壇徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
5 [報告]
發(fā)表于 2011-11-23 09:12 |只看該作者
占位
把握今天 發(fā)表于 2011-11-23 00:06



    希望參與進(jìn)來一起分析!

論壇徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
6 [報告]
發(fā)表于 2011-11-23 20:49 |只看該作者
2樓今天有更新!

論壇徽章:
1
雙子座
日期:2013-11-06 17:18:01
7 [報告]
發(fā)表于 2011-12-02 11:00 |只看該作者
我才知道 原來Nginx是這么小{:3_198:}tar包700K

論壇徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
8 [報告]
發(fā)表于 2011-12-02 11:46 |只看該作者
我才知道 原來Nginx是這么小tar包700K
seufy88 發(fā)表于 2011-12-02 11:00


短小精干

論壇徽章:
0
9 [報告]
發(fā)表于 2011-12-05 07:23 |只看該作者
我來了。
我還以為不分析了呢。
這幾天在看 多線程編程 | 算法 | 離散數(shù)學(xué)。

論壇徽章:
0
10 [報告]
發(fā)表于 2011-12-05 07:24 |只看該作者
本帖最后由 wangzhen11aaa 于 2011-12-05 07:28 編輯

Lz 分析很漂亮。抽時間拜讀。
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP