Nginx 源碼分析:從模塊到配置(上)

源文件路徑

src\core\ngx_conf_file.h
src\core\ngx_conf_file.c

主要內容

本篇的主要目的在於分析Nginx的配置功能。因爲Nginx的配置基本就是對模塊的配置,所以,在討論配置功能以前,須要先分析Nginx的模塊功能。nginx

對於模塊功能,這裏的重點不在於某個模塊的細節,而在於分析Nginx的模塊架構是如何設計的。web

相似地,由於對於Nginx的配置文件,沒有資料比官方文檔上說得更明白,因此,這裏的重點並非對配置文件,而是分析Nginx的配置功能是如何設計的。數組

好了,開始吧。服務器

模塊功能

Nginx的基礎架構是高度模塊化的。能夠從多個方面來觀察Nginx的模塊化。架構

官方文檔首頁的整個下半頁是Nginx自身全部模塊的列表。從這裏能夠看到Nginx的模塊輪廓。框架

若是從源碼安裝Nginx,那麼首先執行的configure腳本的配置參數裏,很大一部分是用來管理是否安裝某個模塊的。固然,Nginx運行必備的模塊是必須安裝的,所以,並不會出如今configure的配置選項裏。模塊化

Nginx自己由多個基本的模塊構成,其中,核心的部分是一個叫ngx_core_module的模塊。固然,對於一個web服務器,僅僅有一個核心是不夠的,還須要大量的「輔助模塊」。這有點像Linux的設計,一堆外圍設施做爲模塊與Linux內核構成整個Linux系統。函數

很天然的會想到,Nginx的這種模塊化設計是支持第三方插件的。這種設計也大大增長了Nginx的彈性和能力。post

模塊的組織和管理

既然Nginx是由許多模塊構成的,那麼,如何組織和管理這些模塊是首先要關注的問題。在Nginx中,使用全局數組ngx_modules保存和管理全部Nginx的模塊ui

如何作到的呢?

首先,Nginx的衆多模塊被分紅兩類:必須安裝的模塊能夠安裝的模塊

  • 必須安裝的模塊是保證Nginx正常功能的模塊,沒得選擇,這些模塊會出如今ngx_modules裏。好比ngx_core_module
  • 能夠安裝的模塊經過configure的配置和系統環境,被有選擇的安裝,這些模塊裏,被選擇安裝的模塊會出如今ngx_modules數組中。

configure執行結束後,會生成一個objs\ngx_modules.c的源代碼文件:
文件內容隨configure配置不一樣而不一樣,例如:

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

extern ngx_module_t  ngx_core_module;
extern ngx_module_t  ngx_errlog_module;
extern ngx_module_t  ngx_conf_module;
extern ngx_module_t  ngx_events_module;
extern ngx_module_t  ngx_event_core_module;
extern ngx_module_t  ngx_epoll_module;
extern ngx_module_t  ngx_regex_module;
extern ngx_module_t  ngx_http_module;
extern ngx_module_t  ngx_http_core_module;
extern ngx_module_t  ngx_http_log_module;
extern ngx_module_t  ngx_http_upstream_module;
extern ngx_module_t  ngx_http_static_module;
extern ngx_module_t  ngx_http_autoindex_module;
extern ngx_module_t  ngx_http_index_module;

...

ngx_module_t *ngx_modules[] = {
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    &ngx_events_module,
    &ngx_event_core_module,
    &ngx_epoll_module,
    &ngx_regex_module,
    &ngx_http_module,
    &ngx_http_core_module,
    &ngx_http_log_module,
    &ngx_http_upstream_module,
    &ngx_http_static_module,
    &ngx_http_autoindex_module,
    &ngx_http_index_module,
   ...
    NULL
};

因爲數組存放的是同一類型的數據。所以,必須有一個模塊基類來抽象的定義全部模塊。這個抽象基類就是ngx_module_t

所以,能夠看到ngx_modules數組裏存放的是ngx_module_t *類型指針,而且,最後一個必定是空指針NULL

將最後一個元素置爲空指針NULL,是爲了遍歷數組的時候,標記數組結束,這樣沒必要在使用時記錄數組大小。

例如:

for (i = 0; ngx_modules[i]; i++) {
    ...
}

使用extern ngx_module_t ngx_core_module;使編譯器在編譯的時候不去處理ngx_core_module,在連接階段,再將ngx_core_module連接到正確的地址中。

相似地,在源文件core\ngx_conf_file.h的末尾聲明外部ngx_modules[]

extern ngx_module_t  *ngx_modules[];

因爲core\ngx_conf_file.h會被看成公共頭文件被包含到其餘文件中,所以,ngx_modules是一個全局變量

======分割線=======
在編譯階段,objs\ngx_modules.c文件中的那一堆extern變量及ngx_modules變量都會被初始化。緣由在於,這些變量都屬於全局變量,將會被靜態初始化
======分割線=======

這樣,有了全局數組ngx_modules,就能夠獲取全部模塊的指針,從而操做全部模塊。對全部模塊的管理和操做也由此展開。

全部模塊的基類 ngx_module_t

全部模塊都是ngx_module_t類型的變量,其定義以下:

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;
};

各成員變量含義以下

1. type

type變量是指模塊的類型。Nginx中模塊的類型定義以下:

#define NGX_CORE_MODULE      0x45524F43  /* "CORE" */
#define NGX_CONF_MODULE      0x464E4F43  /* "CONF" */
#define NGX_EVENT_MODULE     0x544E5645  /* "EVNT" */
#define NGX_HTTP_MODULE      0x50545448  /* "HTTP" */
#define NGX_MAIL_MODULE      0x4C49414D  /* "MAIL" */

也就是說,Nginx共有5中模塊類型CORFCONFEVNTHTTPMAIL

2. index

index變量是指,該模塊在ngx_modules數組中的次序,或者說下標。該變量在Nginx執行初始化的時候被初始化,初始化代碼位於core\nginx.cmain函數中:

ngx_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = ngx_max_module++;
    }

3. ctx_index

ctx_index是指,在ngx_modules數組中,該模塊在相同類型的模塊中的次序。

例如,ngx_modules數組中有多個類型爲NGX_EVENT_MODULE的模塊,那麼其中一個模塊的ctx_index初始化以下:

// 源碼文件event\ngx_event.c
    ngx_event_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
            continue;
        }
        ngx_modules[i]->ctx_index = ngx_event_max_module++;
    }

4. ctx

ctxvoid *指針型變量,這是指向與模塊相關的上下文

這裏先解釋一下什麼叫與模塊相關的上下文

上下文這個概念通常能夠理解爲運行環境,那麼與模塊相關的上下文就是指模塊運行環境。換句話說,就是一些參數的配置信息。由於配置通常意味着設置,而設置的對象就是模塊的運行環境。好比,ngx_core_modules運行時的usergroup,就屬於運行環境,同時,也屬於配置項。

因爲模塊的參數通常比較多,正確設置參數的配置須要不少操做。因此,上下文其實也意味着一組操做,這組操做的目的在於根據配置,正確設置模塊的參數信息。同時,這也意味着,不一樣的模塊,這組操做是不一樣的,所以,這裏須要將ctx設置爲void *指針,來進行泛型的操做。

ngx_core_module爲例,其ctx指向的上下文是這個樣子的:

static ngx_core_module_t  ngx_core_module_ctx = {
    ngx_string("core"),
    ngx_core_module_create_conf,
    ngx_core_module_init_conf
};

其中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_core_module_init_conf函數中,初始化了一個ngx_core_conf_t的結構體。

所以,可知,void *ctx指向了一組操做,這組操做的目的在於初始化一個與模塊配置信息相關的結構體
這裏涉及到了三個結構體:

  • ngx_module_t ngx_core_module:用來表示模塊自己,保存在ngx_modules數組中;
  • ngx_core_conf_t core_conf:用來保存對該模塊的配置信息;
  • ngx_core_module_t ngx_core_module_ctx:用來初始化ngx_core_conf_t中的成員變量;

6. commands

commands是ngx_command_t數組,表示一組配置文件中的可配項(指令)。

例如,在配置文件nginx.confworker_processes 1;對應commands數組中的一項,該項的定義以下,類型爲ngx_command_t

{ ngx_string("worker_processes"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
      ngx_set_worker_processes,
      0,
      0,
      NULL }

其中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;
};

因此,commands的含義在於該模塊的配置項在配置文件中的指令

ngx_module_t 總結

到這裏,一個完整的模塊所需的各個部分逐漸顯露出來了:

  1. ngx_module_t結構體,用來表示模塊的定義,被放入ngx_modules數組中。
  2. ngx_<module name>_conf_t結構體,用來表示模塊的配置項,這個結構體隨模塊的不一樣而不一樣,經過上下文操做及commands中定義的指令,由配置文件肯定其成員值。
  3. ngx_<module>_module_t結構體,表示一組用來初始化ngx_<module name>_conf_t結構體的操做。在Nginx初始化時被調用,初始化ngx_<module name>_conf_t結構體。
  4. ngx_command_t結構體數組,用來表示該模塊能夠在配置文件中配置的項目,及其操做指令。在Nginx初始化解析配置文件後,調用ngx_command_t中的函數set初始化ngx_<module name>_conf_t結構體。

總結

Nginx中,一個模塊運行所需的各類配置參數被封裝成了ngx_<module name>_conf_t結構體。

這個結構體內成員變量的初始化被分紅了兩部分:

  1. 模塊相關的上下文ngx_<module>_module_t:即定義了一組初始化操做,用來將該結構體內的各成員初始化。
  2. 配置文件相關指令ngx_command_t:即規定了配置文件中可使用的指令及其對應的操做函數。

而模塊自己被封裝成了ngx_module_t類型結構體,被用來供Nginx統一管理和使用。

模塊和配置的聯繫很是緊密,所以,這裏僅僅先大略的分析一下模塊功能的總體框架,下一篇中,將在本篇的基礎上分析Nginx的配置功能。

相關文章
相關標籤/搜索