1. 程式人生 > >菜鳥學習nginx之核心模組ngx_events_module

菜鳥學習nginx之核心模組ngx_events_module

既然Nginx是HTTP服務端軟體,那麼必然離不開網路。今天就介紹Nginx高效能網路是如何實現的。

一、核心模組-ngx_events_module (校長)

上一篇中介紹了Nginx模組抽象,今天深入探討一下ngx_event_module事件模組。

1.1、模組定義

/**
 * 模組定義--事件模組
 */
ngx_module_t ngx_events_module = {
    NGX_MODULE_V1,
    &ngx_events_module_ctx, /* module context */
    ngx_events_commands,    /* module directives */
    NGX_CORE_MODULE,        /* module type  事件模組作為核心模組 */
    NULL,                   /* init master */
    NULL,                   /* init module */
    NULL,                   /* init process */
    NULL,                   /* init thread */
    NULL,                   /* exit thread */
    NULL,                   /* exit process */
    NULL,                   /* exit master */
    NGX_MODULE_V1_PADDING};

說明:

1)ngx_events_module型別為NGX_CORE_MODULE屬於核心模組, 可見type賦值

2)模組上下文ctx賦值為ngx_events_module_ctx。其實所有核心模組(NGX_CORE_MODULE)的ctx指向型別都是ngx_core_module_t。

1.2、模組上下文

事件模組上下文定義為ngx_events_module_ctx,具體如下:

/**
 * 核心模組(NGX_CORE_MODULE),上下文
 * 所有核心模組的上下均為該介面
 */
typedef struct {
    ngx_str_t             name; /* 與配置檔案nginx.conf中出現的標籤保持一致 */
    void               *(*create_conf)(ngx_cycle_t *cycle);
    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;

/**
 * 定義模組上下文,當我們新增核心模組時,需要指定ngx_core_module_t作為上下文
 */
static ngx_core_module_t ngx_events_module_ctx = {
    ngx_string("events"),
    NULL,
    ngx_event_init_conf};

說明:

1)所有核心模組上下文均抽象為ngx_core_module_t,例如:HTTP模組ngx_http_module 。

2)name名稱,當在nginx.conf檔案出現與name相匹配的,則表示是該模組。如下所示:

//nginx.conf配置檔案
events {
    worker_connections  1024;
}

1.3、命令字解析

當出現上面定義的標籤,那麼Nginx是如何解析該標籤呢?可參考命令字結構體

/**
 * 命令字解析
 */
struct ngx_command_s {
    ngx_str_t             name; /* 標籤名字 即出現nginx*/
    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;
};

/** 
 * 解析命令字 即解析配置檔案 當配置檔案出現標籤events時呼叫ngx_events_block方法 
 */
static ngx_command_t ngx_events_commands[] = {

    {ngx_string("events"),
     NGX_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_NOARGS,
     ngx_events_block,
     0,
     0,
     NULL},

    ngx_null_command};

欄位名稱

含義

備註

name

出現在配置檔案中配置項名字

必須指定

type

當前配置項所屬範圍

1、可取值NGX_HTTP_MAIN_CONF,NGX_HTTP_SRV_CONF,

NGX_CONF_NOARGS等。

2、可有多值,當存在多值需進行或操作,必須指定

set

回撥方法,主要用於解析當前項

必須指定

conf

表示儲存位置

1、開發非http模組時設定為0

2、基於http模組開發時,可取值為NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET中之一。舉例說明:若儲存該配置項的資料結構是由回撥函式create_main_conf(struct ngx_http_module_t)生成則設定為NGX_HTTP_MAIN_CONF_OFFSE

offset

當前配置項,在資料結構中偏移量

1、一般場景下,一個配置項會對應結構體中一個變數,此處offset取值就是該變數相對於結構體起始地址的偏移量

2、必須指定

post

一般不用設定為NULL

 

說明:

當配置檔案中出現events標籤則呼叫ngx_events_block回撥進行解析。 

1.4、模組載入流程

簡單回顧一下Nginx啟動流程。Nginx啟動時候會進入ngx_init_cycle函式,在這個函式中會初始化module,部分程式碼如下:

    /* 初始化核心模組即型別為NGX_CORE_MODULE */
    for (i = 0; cycle->modules[i]; i++)
    {
        if (cycle->modules[i]->type != NGX_CORE_MODULE)
        {
            continue;
        }
            
        module = cycle->modules[i]->ctx;/* 定義模組時賦值 */

        if (module->create_conf)
        {
            /**
             * 目前定義create_conf回撥方法 只有ngx_core_module和ngx_regex_module
             * ngx_event_module沒有定義create_conf,只定義了init_conf,可知
             * ngx_event_module對應的conf_ctx是NULL, 但是在經過ngx_conf_parse後
             * conf_ctx不為NULL. 
             */
            rv = module->create_conf(cycle);
            if (rv == NULL)
            {
                ngx_destroy_pool(pool);
                return NULL;
            }
            cycle->conf_ctx[cycle->modules[i]->index] = rv;//給指標陣列賦值
        }
    }

    ...

    if (ngx_conf_param(&conf) != NGX_CONF_OK)
    {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }
    /**
     * 解析配置檔案 
     * 注1: 經過此方法之後 核心模組ngx_event_module對應的conf_ctx有資料了 
     * 在執行ngx_conf_parse函式時,會解析nginx.conf配置檔案,當遇到event標籤,會呼叫
     * ngx_events_block回撥方法 該方法會設定conf_ctx
     * 注2: 經過此方法cycle->listening中會儲存真正資料
     */
    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK)
    {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }

    if (ngx_test_config && !ngx_quiet_mode)
    {
        ngx_log_stderr(0, "the configuration file %s syntax is ok",
                       cycle->conf_file.data);
    }

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

        module = cycle->modules[i]->ctx;

        if (module->init_conf)
        {
            if (module->init_conf(cycle,
                                  cycle->conf_ctx[cycle->modules[i]->index]) == NGX_CONF_ERROR)
            {
                environ = senv;
                ngx_destroy_cycle_pools(&conf);
                return NULL;
            }
        }
    }

通過上面程式碼可知,會分別呼叫核心模組中定義的create_conf和init_conf回撥函式。針對於ngx_events_module來看,只實現了init_conf回撥函式,ngx_events_module並沒有執行具體邏輯,只是單純檢查了一下,程式碼如下:

static char *
ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)
{
    if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL)
    {
        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                      "no \"events\" section in configuration");
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}

 那麼什麼時候呼叫ngx_events_block呢?初學者不太清楚的話,有一個技巧可以幫助我們--GDB。通過GDB打斷點,進行除錯如下所示:


[[email protected] ~]# gdb /usr/local/nginx/sbin/nginx
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-ZTEOS-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/local/nginx/sbin/nginx...done.
(gdb) b ngx_events_block
Breakpoint 1 at 0x429a8b: file src/event/ngx_event.c, line 965.
(gdb) set follow-fork-mode child
(gdb) r
Starting program: /usr/local/nginx/sbin/nginx
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, ngx_events_block (cf=0x7fffffffe180, cmd=0x69f960 <ngx_events_commands>, conf=0x6d2070) at src/event/ngx_event.c:965
965     src/event/ngx_event.c: No such file or directory.
Missing separate debuginfos, use: debuginfo-install glibc-2.17-157.el7_3.4.cgsl1342.x86_64 nss-softokn-freebl-3.28.3-8.el7_4.x86_64 pcre-8.32-17.el7.x86_64 zlib-1.2.7-17.el7.x86_64
(gdb) bt
#0  ngx_events_block (cf=0x7fffffffe180, cmd=0x69f960 <ngx_events_commands>, conf=0x6d2070) at src/event/ngx_event.c:965
#1  0x000000000041ff88 in ngx_conf_handler (last=1, cf=0x7fffffffe180) at src/core/ngx_conf_file.c:500
#2  ngx_conf_parse ([email protected]=0x7fffffffe180, [email protected]=0x6d1270) at src/core/ngx_conf_file.c:333
#3  0x000000000041d704 in ngx_init_cycle ([email protected]=0x7fffffffe230) at src/core/ngx_cycle.c:293
#4  0x000000000040c7e3 in main (argc=<optimized out>, argv=<optimized out>) at src/core/nginx.c:284
(gdb)

通過堆疊可知,是在執行ngx_init_cycle函式時呼叫ngx_conf_parse,最終呼叫到ngx_events_block。其實就是在解析配置檔案時。接下來看一下ngx_events_block函式定義:

/**
 * 針對events標籤進行解析
 * @param cf  配置結構
 * @param cmd 命令字對映表
 * @param conf 配置結構
 */
static char *
ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char *rv;
    void ***ctx;
    ngx_uint_t i;
    ngx_conf_t pcf;
    ngx_event_module_t *m;

    if (*(void **)conf)
    {
        return "is duplicate";
    }

    /* count the number of the event modules and set up their indices */
    /* 獲取所有event模組數量並且設定他們的索引值ctx_index             */
    ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);

    ctx = ngx_pcalloc(cf->pool, sizeof(void *));
    if (ctx == NULL)
    {
        return NGX_CONF_ERROR;
    }

    *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
    if (*ctx == NULL)
    {
        return NGX_CONF_ERROR;
    }

    *(void **)conf = ctx;
    /* 呼叫模組的create_conf方法,模組型別必須是NGX_EVENT_MODULE */
    for (i = 0; cf->cycle->modules[i]; i++)
    {
        if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE)
        {
            continue;
        }

        m = cf->cycle->modules[i]->ctx;

        if (m->create_conf)
        {// ctx_index是在上方ngx_count_modules中設定
            (*ctx)[cf->cycle->modules[i]->ctx_index] =
                m->create_conf(cf->cycle);
            if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL)
            {
                return NGX_CONF_ERROR;
            }
        }
    }

    pcf = *cf;
    cf->ctx = ctx;
    cf->module_type = NGX_EVENT_MODULE;
    cf->cmd_type = NGX_EVENT_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    if (rv != NGX_CONF_OK)
    {
        return rv;
    }
    /* 呼叫模組的init_conf方法,模組型別必須是NGX_EVENT_MODULE */
    for (i = 0; cf->cycle->modules[i]; i++)
    {
        if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE)
        {
            continue;
        }

        m = cf->cycle->modules[i]->ctx;

        if (m->init_conf)
        {
            rv = m->init_conf(cf->cycle,
                              (*ctx)[cf->cycle->modules[i]->ctx_index]);
            if (rv != NGX_CONF_OK)
            {
                return rv;
            }
        }
    }

    return NGX_CONF_OK;
}

 這個地方需要特別注意,這裡呼叫create_conf和init_conf回撥函式,是型別為NGX_EVENT_MODULE的模組所定義的。在Linux環境下,呼叫的是ngx_event_core_module、ngx_epoll_module所定義的。

二、事件模組-ngx_event_core_module(班主任)

上面介紹了,核心模組ngx_events_module,接下來介紹事件模組(NGX_EVENT_MODULE)中ngx_event_core_module。雖然名字中有core,但並不是核心模組。

2.1、模組定義

static ngx_str_t event_core_name = ngx_string("event_core");

static ngx_command_t ngx_event_core_commands[] = {

    {ngx_string("worker_connections"),
     NGX_EVENT_CONF | NGX_CONF_TAKE1,
     ngx_event_connections,
     0,
     0,
     NULL},

    {ngx_string("use"),
     NGX_EVENT_CONF | NGX_CONF_TAKE1,
     ngx_event_use,
     0,
     0,
     NULL},

    {ngx_string("multi_accept"),
     NGX_EVENT_CONF | NGX_CONF_FLAG,
     ngx_conf_set_flag_slot,
     0,
     offsetof(ngx_event_conf_t, multi_accept),
     NULL},

    {ngx_string("accept_mutex"),
     NGX_EVENT_CONF | NGX_CONF_FLAG,
     ngx_conf_set_flag_slot,
     0,
     offsetof(ngx_event_conf_t, accept_mutex),
     NULL},

    {ngx_string("accept_mutex_delay"),
     NGX_EVENT_CONF | NGX_CONF_TAKE1,
     ngx_conf_set_msec_slot,
     0,
     offsetof(ngx_event_conf_t, accept_mutex_delay),
     NULL},

    {ngx_string("debug_connection"),
     NGX_EVENT_CONF | NGX_CONF_TAKE1,
     ngx_event_debug_connection,
     0,
     0,
     NULL},

    ngx_null_command};

static ngx_event_module_t ngx_event_core_module_ctx = {
    &event_core_name,
    ngx_event_core_create_conf, /* create configuration */
    ngx_event_core_init_conf,   /* init configuration */

    {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};

ngx_module_t ngx_event_core_module = {
    NGX_MODULE_V1,
    &ngx_event_core_module_ctx, /* module context */
    ngx_event_core_commands,    /* module directives */
    NGX_EVENT_MODULE,           /* module type */
    NULL,                       /* init master */
    ngx_event_module_init,      /* init module */
    ngx_event_process_init,     /* init process */
    NULL,                       /* init thread */
    NULL,                       /* exit thread */
    NULL,                       /* exit process */
    NULL,                       /* exit master */
    NGX_MODULE_V1_PADDING};

2.2、模組實現

對於上面ngx_event_core_module模組,上面回撥函式呼叫先後順序為:create_conf,init_conf,init_module,init_process。下面來看一下,4個回撥函式具體實現內容:

/**
 * 建立event conf結構
 * @param cycle 核心結構體
 */
static void *
ngx_event_core_create_conf(ngx_cycle_t *cycle)
{
    ngx_event_conf_t *ecf;

    ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
    if (ecf == NULL)
    {
        return NULL;
    }
    /* 與命令字保持一致 初始化為-1*/
    ecf->connections = NGX_CONF_UNSET_UINT;
    ecf->use = NGX_CONF_UNSET_UINT;
    ecf->multi_accept = NGX_CONF_UNSET;
    ecf->accept_mutex = NGX_CONF_UNSET;
    ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
    ecf->name = (void *)NGX_CONF_UNSET;

#if (NGX_DEBUG)

    if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,
                       sizeof(ngx_cidr_t)) == NGX_ERROR)
    {
        return NULL;
    }

#endif

    return ecf;
}
static char *
ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
{
    ngx_event_conf_t *ecf = conf;
    /* linux下面使用epoll模型 */
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
    int fd;
#endif
    ngx_int_t i;
    ngx_module_t *module;
    ngx_event_module_t *event_module;

    module = NULL;

#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)

    fd = epoll_create(100); //建立epoll物件 此處100沒有實際意義

    if (fd != -1)
    {
        (void)close(fd);//此處只是表示epoll模型可用 在ngx_epoll_init函式中真正建立
        module = &ngx_epoll_module;
    }
    else if (ngx_errno != NGX_ENOSYS)
    {
        module = &ngx_epoll_module;
    }

#endif

#if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL)

    module = &ngx_devpoll_module;

#endif

#if (NGX_HAVE_KQUEUE)

    module = &ngx_kqueue_module;

#endif

#if (NGX_HAVE_SELECT)
    /* windows下面使用select模型 */
    if (module == NULL)
    {
        module = &ngx_select_module;
    }

#endif

    if (module == NULL)
    {
        for (i = 0; cycle->modules[i]; i++)
        {

            if (cycle->modules[i]->type != NGX_EVENT_MODULE)
            {
                continue;
            }

            event_module = cycle->modules[i]->ctx;

            if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)
            {
                continue;
            }

            module = cycle->modules[i];
            break;
        }
    }

    if (module == NULL)
    {
        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
        return NGX_CONF_ERROR;
    }
    /* 初始化變數 使用預設值*/
    ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
    cycle->connection_n = ecf->connections;

    ngx_conf_init_uint_value(ecf->use, module->ctx_index);

    event_module = module->ctx;
    ngx_conf_init_ptr_value(ecf->name, event_module->name->data);

    ngx_conf_init_value(ecf->multi_accept, 0);
    ngx_conf_init_value(ecf->accept_mutex, 0);
    ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);

    return NGX_CONF_OK;
}

 對於create_conf和init_conf來說,這兩個回撥函式是圍繞ngx_event_conf_t進行分配、初始化。接下來看一下,init_module回撥函式實現:

/**
 * Event模組初始化
 */
static ngx_int_t
ngx_event_module_init(ngx_cycle_t *cycle)
{
    void ***cf;
    u_char *shared;
    size_t size, cl;
    ngx_shm_t shm;
    ngx_time_t *tp;
    ngx_core_conf_t *ccf;
    ngx_event_conf_t *ecf;
    /* 獲取頂級型別的conf 再獲取子級conf */
    cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
    ecf = (*cf)[ngx_event_core_module.ctx_index];

    if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER)
    {
        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                      "using the \"%s\" event method", ecf->name);
    }

    ccf = (ngx_core_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ngx_timer_resolution = ccf->timer_resolution;

#if !(NGX_WIN32)
    {
        ngx_int_t limit;
        struct rlimit rlmt;

        if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "getrlimit(RLIMIT_NOFILE) failed, ignored");
        }
        else
        {
            if (ecf->connections > (ngx_uint_t)rlmt.rlim_cur && (ccf->rlimit_nofile == NGX_CONF_UNSET || ecf->connections > (ngx_uint_t)ccf->rlimit_nofile))
            {
                limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ? (ngx_int_t)rlmt.rlim_cur : ccf->rlimit_nofile;

                ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                              "%ui worker_connections exceed "
                              "open file resource limit: %i",
                              ecf->connections, limit);
            }
        }
    }
#endif /* !(NGX_WIN32) */

    if (ccf->master == 0)
    {
        return NGX_OK;
    }

    if (ngx_accept_mutex_ptr)
    {
        return NGX_OK;
    }

    /* 設定為快取大小,提高讀寫 */
    /* cl should be equal to or greater than cache line size */
    cl = 128;
    /* 分配共享記憶體 */
    size = cl    /* ngx_accept_mutex */
           + cl  /* ngx_connection_counter */
           + cl; /* ngx_temp_number */

#if (NGX_STAT_STUB)

    size += cl    /* ngx_stat_accepted */
            + cl  /* ngx_stat_handled */
            + cl  /* ngx_stat_requests */
            + cl  /* ngx_stat_active */
            + cl  /* ngx_stat_reading */
            + cl  /* ngx_stat_writing */
            + cl; /* ngx_stat_waiting */

#endif
    /* 分配共享記憶體 呼叫mmap */
    shm.size = size;
    ngx_str_set(&shm.name, "nginx_shared_zone");
    shm.log = cycle->log;

    if (ngx_shm_alloc(&shm) != NGX_OK)
    {
        return NGX_ERROR;
    }

    shared = shm.addr;
    /* 初始化共享記憶體 共享記憶體主要用程序間通訊 */
    ngx_accept_mutex_ptr = (ngx_atomic_t *)shared;
    ngx_accept_mutex.spin = (ngx_uint_t)-1; //對於解決驚群問題 spin設定為-1 保證程序不睡眠

    if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *)shared,
                         cycle->lock_file.data) != NGX_OK)
    {
        return NGX_ERROR;
    }

    ngx_connection_counter = (ngx_atomic_t *)(shared + 1 * cl);

    (void)ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "counter: %p, %uA",
                   ngx_connection_counter, *ngx_connection_counter);

    ngx_temp_number = (ngx_atomic_t *)(shared + 2 * cl);

    tp = ngx_timeofday();

    ngx_random_number = (tp->msec << 16) + ngx_pid;

#if (NGX_STAT_STUB)

    ngx_stat_accepted = (ngx_atomic_t *)(shared + 3 * cl);
    ngx_stat_handled = (ngx_atomic_t *)(shared + 4 * cl);
    ngx_stat_requests = (ngx_atomic_t *)(shared + 5 * cl);
    ngx_stat_active = (ngx_atomic_t *)(shared + 6 * cl);
    ngx_stat_reading = (ngx_atomic_t *)(shared + 7 * cl);
    ngx_stat_writing = (ngx_atomic_t *)(shared + 8 * cl);
    ngx_stat_waiting = (ngx_atomic_t *)(shared + 9 * cl);

#endif

    return NGX_OK;
}

該函式主要是設定共享記憶體,因為Nginx是單執行緒多程序模式,為了程序間通訊,此處採用共享記憶體方式。共享記憶體主要實現功能有:互斥鎖,全域性資料統計(例如統計模組)。下面來看一下init_process回撥函式實現,由於該函式實現程式碼較長,這裡分批實現:

static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
    ngx_uint_t m, i;
    ngx_event_t *rev, *wev;
    ngx_listening_t *ls;
    ngx_connection_t *c, *next, *old;
    ngx_core_conf_t *ccf;
    ngx_event_conf_t *ecf;
    ngx_event_module_t *module;

    ccf = (ngx_core_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);

    if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex)
    {
        ngx_use_accept_mutex = 1;
        ngx_accept_mutex_held = 0;
        ngx_accept_mutex_delay = ecf->accept_mutex_delay;
    }
    else
    {
        ngx_use_accept_mutex = 0;
    }

#if (NGX_WIN32)

    /*
     * disable accept mutex on win32 as it may cause deadlock if
     * grabbed by a process which can't accept connections
     */

    ngx_use_accept_mutex = 0;

#endif
    /* 初始化全域性佇列 儲存接收到網路事件 */
    ngx_queue_init(&ngx_posted_accept_events); //儲存accept事件 優先順序高
    ngx_queue_init(&ngx_posted_events); //除accept事件以外的 優先順序低
    /* 初始化定時器紅黑樹 */
    if (ngx_event_timer_init(cycle->log) == NGX_ERROR)
    {
        return NGX_ERROR;
    }

    for (m = 0; cycle->modules[m]; m++)
    {
        if (cycle->modules[m]->type != NGX_EVENT_MODULE)
        {
            continue;
        }

        if (cycle->modules[m]->ctx_index != ecf->use)
        {
            continue;
        }

        /**
         * 在任何作業系統下 執行Nginx 網路模型始終有一個 所以下面直接break 
         * linux系統下面是epoll模型,init回撥方法是ngx_epoll_init
         */
        module = cycle->modules[m]->ctx;            
        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK)
        {
            /* fatal */
            exit(2);
        }

        break;
    }

 這部分程式碼就是初始化一些資料,其中最終需要是呼叫init回撥函式。在註釋中也寫的比較明確,任何網路模型(epoll,select,devpoll等)都需要實現init函式。在linux環境下使用的網路模型是epoll,init回撥函式是ngx_epoll_init。注:ngx_epoll_init函式主要工作是建立epoll物件,開啟事件驅動。

    /**
     * nginx.conf開啟毫秒定時器精度
     */
    if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT))
    {
        struct sigaction sa;
        struct itimerval itv;
        /* 註冊訊號 */
        ngx_memzero(&sa, sizeof(struct sigaction));
        sa.sa_handler = ngx_timer_signal_handler;
        sigemptyset(&sa.sa_mask);

        if (sigaction(SIGALRM, &sa, NULL) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "sigaction(SIGALRM) failed");
            return NGX_ERROR;
        }

        itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
        itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
        itv.it_value.tv_sec = ngx_timer_resolution / 1000;
        itv.it_value.tv_usec = (ngx_timer_resolution % 1000) * 1000;
        //啟動定時器 當超時後會產生SIGALRM訊號
        if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "setitimer() failed");
        }
    }
    //設定門限值
    if (ngx_event_flags & NGX_USE_FD_EVENT)
    {
        struct rlimit rlmt;

        if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "getrlimit(RLIMIT_NOFILE) failed");
            return NGX_ERROR;
        }

        cycle->files_n = (ngx_uint_t)rlmt.rlim_cur;

        cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,
                                  cycle->log);
        if (cycle->files == NULL)
        {
            return NGX_ERROR;
        }
    }
    /* 建立連線池 預設1024 */
    cycle->connections =
        ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
    if (cycle->connections == NULL)
    {
        return NGX_ERROR;
    }

    c = cycle->connections;
    /* 為每個連線建立一個讀事件佇列 儲存在核心結構體 */
    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                   cycle->log);
    if (cycle->read_events == NULL)
    {
        return NGX_ERROR;
    }

    rev = cycle->read_events;
    for (i = 0; i < cycle->connection_n; i++)
    {
        rev[i].closed = 1;
        rev[i].instance = 1;
    }
    /* 為每個連線建立一個寫事件佇列 儲存在核心結構體 */
    cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
                                    cycle->log);
    if (cycle->write_events == NULL)
    {
        return NGX_ERROR;
    }

    wev = cycle->write_events;
    for (i = 0; i < cycle->connection_n; i++)
    {
        wev[i].closed = 1;
    }
    /* 初始化連線connection物件 */
    i = cycle->connection_n;
    next = NULL;

    do
    {
        i--;

        c[i].data = next;
        c[i].read = &cycle->read_events[i];
        c[i].write = &cycle->write_events[i];
        c[i].fd = (ngx_socket_t)-1;

        next = &c[i];
    } while (i);
    /* 可用連線connection設定 */
    cycle->free_connections = next;
    cycle->free_connection_n = cycle->connection_n;

    /**
     * for each listening socket 
     * 迴圈遍歷listening 將listen socket 與connection物件進行繫結
     */
    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++)
    {

#if (NGX_HAVE_REUSEPORT)
        if (ls[i].reuseport && ls[i].worker != ngx_worker)
        {
            continue;
        }
#endif
        /* 返回可用連線物件 這裡listening socket也會佔用一個connection物件 */
        c = ngx_get_connection(ls[i].fd, cycle->log);

        if (c == NULL)
        {
            return NGX_ERROR;
        }

        c->type = ls[i].type;
        c->log = &ls[i].log;

        c->listening = &ls[i];
        ls[i].connection = c;

        rev = c->read;

        rev->log = c->log;
        rev->accept = 1;

#if (NGX_HAVE_DEFERRED_ACCEPT)
        rev->deferred_accept = ls[i].deferred_accept;
#endif

        if (!(ngx_event_flags & NGX_USE_IOCP_EVENT))
        {
            if (ls[i].previous)
            {

                /*
                 * delete the old accept events that were bound to
                 * the old cycle read events array
                 */

                old = ls[i].previous->connection;

                if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR)
                {
                    return NGX_ERROR;
                }

                old->fd = (ngx_socket_t)-1;
            }
        }

#if (NGX_WIN32)

        ...

#else
        /* 事件發生後 處理函式 */
        rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
                                                : ngx_event_recvmsg;

#if (NGX_HAVE_REUSEPORT)

        if (ls[i].reuseport)
        {
            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR)
            {
                return NGX_ERROR;
            }

            continue;
        }

#endif

        if (ngx_use_accept_mutex)
        {
            continue;
        }

#if (NGX_HAVE_EPOLLEXCLUSIVE)

        ...

#endif
        //註冊讀事件 主要是用於listen監聽
        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR)
        {
            return NGX_ERROR;
        }

#endif
    }

 這段程式碼主要做兩件事情:

1)開闢connection物件,具體大小由nginx.conf檔案設定

2)將監聽socket註冊到epoll事件驅動中並且設定回撥函式handler

三、總結

本篇主要介紹ngx_event_core_module模組載入以及初始化流程,在這部分程式碼中我們需要了解到,listenning socket是如何加到事件驅動以及Nginx對於連線物件的管理是如何實現的。下一篇介紹ngx_epoll_module