Nginx 源碼分析-- 淺談對模塊module 的基本認知


  分析nginx源碼,談到模塊module是必然的。縱觀nginx源碼,可以說模塊module機制是整個nginx的骨架。因此,對nginx的源碼進行分析,那么對模塊module就需要有一個基本的認知。在淺談開始,我們要明確nginx 模塊構架是從編譯階段開始的,不像apache那樣可以動態的添加模塊,nginx使用的是靜態模塊。這應該也是nginx 為何效率高的原因之一。對nginx的模塊認知,必須要提到一篇大大有名的文章,我想也是每個分析nginx源碼的人都拜讀過的文章《Emiller's Guide To Nginx Module Development 》里面的內容雖然少了點,但講得非常經典,因此特別在這里推薦一個。

  對模塊module的認知,先來看三個數據結構。模塊指令數據結構(ngx_command_t)、模塊定義數據結構(ngx_module_t)、模塊核心數據結構。模塊核心數據結構具體來說可以分為4大類:

  NGX_CORE_MODULENGX_CONF_MODULENGX_EVENT_MODULE 和 NGX_HTTP_MODULE。對於這個數據結構本文選取其中的NGX_CORE_MODULE進行說明。

1、模塊指令數據結構(ngx_command_t

定義如下:

struct ngx_command_s {
    ngx_str_t             name;  
    ngx_uint_t            type;  
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    ngx_uint_t            conf;  
    ngx_uint_t            offset;
    void                 *post;  
};

  name指代命令的類型,具體來說就是配置文件中的配置的參數名。type 為命令的類型如:在主配置文件中配置該命令參數為NGX_HTTP_MAIN_CONF。參數*(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 為執行該命令的函數。conf 為配置信息,offset偏移量,最后一個基本上都是NULL

2、模塊定義數據結構(ngx_module_t

View Code
struct ngx_module_s {
    ngx_uint_t            ctx_index;
    ngx_uint_t            index;

    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            spare2;
    ngx_uint_t            spare3;

    ngx_uint_t            version;

    void                 *ctx;
    ngx_command_t        *commands;
    ngx_uint_t            type;

    ngx_int_t           (*init_master)(ngx_log_t *log);

    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
    void                (*exit_thread)(ngx_cycle_t *cycle);
    void                (*exit_process)(ngx_cycle_t *cycle);

    void                (*exit_master)(ngx_cycle_t *cycle);

    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};

  這個結構體參數比較多,但似乎其中的很多都沒什么太大用。這里說明幾個常用的參數基本認識一下,index 在所有模塊中的位置,可以說是游標,因為nginx所有的模塊都放在了一個模塊數組中ngx_modules[]中(詳情見后文)不需要自己設置,自動生成的。*ctx 指向模塊核心數據結構的指針(每個模塊必備),*commands指向模塊指令數據結構的指針。type模塊的類型,接下來幾個函數指針就是,在某些特定時候需要調用該模塊完成些功能,如(*init_master)(ngx_log_t *log) 即在master初始化時調用,其他類是。

3、模塊核心數據結構

  前面已經說明了,這種數據結構有4大類,這里選取 ngx_core_module_t 來進行簡單認知。 

    typedef struct {

        ngx_str_t             name;  /*名字*/
        void               *(*create_conf)(ngx_cycle_t *cycle);  /*創建時調用*/
        char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);    /*初始化時調用*/
    } ngx_core_module_t;

  認識完以上三種數據結構,那么我們開始好奇這三種數據結構是怎樣嵌入到源碼中工作的呢?如果有記得的在前文中提到了ngx_modules[]這樣的一個數組,但是在源碼中卻沒有?不錯,源碼中確實沒有ngx_modules[]這樣的一個數組,但請不要忘了nginx的軟件構建是從編譯開始的。怎么理解?簡單的理解,就是部分源碼結構體是需要在configure 執行后生成的。那么把模塊嵌入nginx中的工作就交給configure吧!在執行完configure后,打開objs文件夾看到一個ngx_modules.c的文件,好奇的,趕緊打開看看,里面就有你想知道的內容!

ngx_module_t *ngx_modules[] = {

&ngx_core_module,

&ngx_errlog_module,

}

  明白了吧!模塊的編譯后的地址都在這里,是不是感慨nginx精妙的構建!說到這里,你可能還是似懂非懂,那么我自己來寫一個模塊module 來對介紹的基本認知進行理性的看待,也加深對前面所說的理解,學以致用!這里寫的是ngx_core_module_t 類型模塊,核心模塊。為了突出主干,並沒有寫什么功能僅演示下而已。首先,在src文件夾下建立test文件夾,后添加test.c源文件,代碼如下

 

#include <ngx_config.h>
#include <ngx_core.h>


static void *ngx_test_module_create_conf(ngx_cycle_t *cycle);
char * ngx_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static ngx_command_t ngx_test_commands[] = {
     {   
     ngx_string("commands"),
         NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_NOARGS,
/*
第一參數,模塊中沒有初始化函數;
第二參數,nginx.conf配置文件的指令配置類型,就像worker_processes;
第三個參數,該指令后面沒有參數
*/ ngx_test,
0, 0, NULL }, ngx_null_command /*命令集結束*/ }; static ngx_core_module_t ngx_test_module_ctx = { ngx_string("test"), ngx_test_module_create_conf, NULL }; ngx_module_t ngx_test_module = { NGX_MODULE_V1, &ngx_test_module_ctx, ngx_test_commands, NGX_CORE_MODULE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING }; char * ngx_test(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { FILE *fp = fopen("test", "w"); fprintf(fp,"call ngx_test\n"); fclose(fp); return NGX_CONF_OK; } static void *ngx_test_module_create_conf(ngx_cycle_t *cycle){ printf("call ngx_test_module_create_conf\n"); }

 

  對代碼進行簡單說明下,三大結構體前文已經說了,代碼功能為:顯示模塊創建是調用了 ngx_test_module_create_conf,若配置文件中有 commands 指令就會在nginx所在目錄下創建test文件,並向其中寫入call ngx_test這個字符串。

  建立 config 文件,主要用於configure時將我們寫的test模塊包括進去。內容如下:

ngx_addon_name=ngx_test_module
CORE_MODULES="$CORE_MODULES ngx_test_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/test.c"

  三條語句,三個功能:指定添加模塊名,指定模塊類型,指定模塊源文件路徑

  然后,我們執行configure 注意添加參數,加入我們編寫的test模塊:

  ./configure --add-module=src/test

  執行過程中,可以發現

  configuring additional modules
  adding module in src/test
  + ngx_test_module was configured

  這條信息即可表示我們編寫的test模塊已經添加進編譯過程了。如果比較好奇,nginx中有多少默認(直接configure不添加其他參數)的核心模塊,我們不妨找到源碼中的 ngx_cycle.c (本人用的是 nginx 1.3.0版本代碼)文件,將其中添加上一條printf語句,詳細如下:

    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

        module = ngx_modules[i]->ctx;
    printf("core modules:  %s \n",module->name.data);
        if (module->create_conf) {
            rv = module->create_conf(cycle);
            if (rv == NULL) {
                ngx_destroy_pool(pool);
                return NULL;
            }
            cycle->conf_ctx[ngx_modules[i]->index] = rv;
        }
    }

  好了,工作基本完成了,make一下,然后sudo make install哦,對了,別忘記了在nginx.conf 中 加上 “ commands; ”,要不然我們寫的 ngx_command_t 不會執行了。運行nginx后,運行結果如下:

自定義nginx核心模塊運行結果

自定義nginx核心模塊運行結果

自定義nginx核心模塊運行結果

圖1 自定義nginx核心模塊運行結果

  閱讀到這里,我想大家應該對nginx 中的 module 有了一個基本的認知,要想了解nginx的運行機制這點應該很重要的。如果要對源碼進行分析,本文提到的幾個結構體就應該更加要熟悉了。nginx 中的功能都是以模塊的形式進行開發的,如events、http等,就是日志系統(errlog)也是用模塊實現,即使並不想分析源碼有了這些基本的認知對於理解配置nginx也應有很大的幫助!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM