講解nginx事件模塊的實(shí)現(xiàn)細(xì)節(jié)

在《關(guān)于nginx事件模塊結(jié)構(gòu)體的詳解》這篇文章中,我們講解nginx的事件模塊的整體工作流程,并且著重講解了組織事件模塊的各個(gè)方法的作用,本文則主要圍繞這整個(gè)流程,從源碼的角度講解nginx事件模塊的實(shí)現(xiàn)細(xì)節(jié)。

講解nginx事件模塊的實(shí)現(xiàn)細(xì)節(jié)

1. ngx_events_block()—-events配置塊解析

nginx在解析nginx.conf配置文件時(shí),如果當(dāng)前解析的配置項(xiàng)名稱為events,并且是一個(gè)配置塊,則會調(diào)用ngx_events_block()方法解析該配置塊,如下是該方法的源碼:

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; ??//?如果存儲事件模塊配置數(shù)據(jù)的配置項(xiàng)不為空,說明已經(jīng)解析過配置項(xiàng)了,因而直接返回 ??if?(*(void?**)?conf)?{ ????return?"is?duplicate"; ??} ??//?這里主要是計(jì)算event模塊的個(gè)數(shù),并且將各個(gè)event模塊的相對順序標(biāo)記在了該模塊的ctx_index屬性中 ??ngx_event_max_module?=?ngx_count_modules(cf->cycle,?NGX_EVENT_MODULE); ??//?創(chuàng)建一個(gè)存儲配置項(xiàng)數(shù)組的指針 ??ctx?=?ngx_pcalloc(cf->pool,?sizeof(void?*)); ??if?(ctx?==?NULL)?{ ????return?NGX_CONF_ERROR; ??} ??//?為配置項(xiàng)指針申請數(shù)組內(nèi)存 ??*ctx?=?ngx_pcalloc(cf->pool,?ngx_event_max_module?*?sizeof(void?*)); ??if?(*ctx?==?NULL)?{ ????return?NGX_CONF_ERROR; ??} ??//?將數(shù)組值賦值到conf中,也即關(guān)聯(lián)到核心配置對象ngx_cycle_t中 ??*(void?**)?conf?=?ctx; ??for?(i?=?0;?cf->cycle->modules[i];?i++)?{ ????if?(cf->cycle->modules[i]->type?!=?NGX_EVENT_MODULE)?{ ??????continue; ????} ????m?=?cf->cycle->modules[i]->ctx; ????//?如果當(dāng)前模塊的create_conf()方法不為空,則調(diào)用該方法創(chuàng)建存儲配置項(xiàng)的結(jié)構(gòu)體 ????if?(m->create_conf)?{ ??????(*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; ??????} ????} ??} ??//?這里將*cf結(jié)構(gòu)體進(jìn)行了復(fù)制,臨時(shí)存儲在pcf中,然后初始化當(dāng)前的*cf結(jié)構(gòu)體的模塊相關(guān)的參數(shù), ??//?以進(jìn)行下一步的解析 ??pcf?=?*cf; ??cf->ctx?=?ctx; ??cf->module_type?=?NGX_EVENT_MODULE; ??cf->cmd_type?=?NGX_EVENT_CONF; ??//?解析events{}配置塊中的子配置項(xiàng) ??rv?=?ngx_conf_parse(cf,?NULL); ??//?重新將pcf復(fù)制給*cf,以供后面返回使用 ??*cf?=?pcf; ??if?(rv?!=?NGX_CONF_OK)?{ ????return?rv; ??} ??//?到這里,說明events{}配置塊的配置項(xiàng)都解析完成了,因而這里調(diào)用各個(gè)模塊的init_conf()方法, ??//?進(jìn)行配置項(xiàng)的初始化和合并工作 ??for?(i?=?0;?cf->cycle->modules[i];?i++)?{ ????if?(cf->cycle->modules[i]->type?!=?NGX_EVENT_MODULE)?{ ??????continue; ????} ????m?=?cf->cycle->modules[i]->ctx; ????//?如果當(dāng)前模塊的init_conf()不為空,則調(diào)用其init_conf()方法初始化配置項(xiàng) ????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; }

ngx_events_block()方法主要完成的工作有如下幾個(gè):

●?調(diào)用ngx_count_modules()方法對事件模塊序號進(jìn)行標(biāo)記,需要注意的是,這里的排序是針對當(dāng)前模塊在所有事件類型模塊中的順序進(jìn)行標(biāo)記,并且將序號保存在各模塊的ctx_index屬性中,比如這里的事件類型核心模塊ngx_event_core_module的ctx_index就為0;

●?為指針ctx申請內(nèi)存空間,并且申請一個(gè)數(shù)組,將其地址賦值給ctx指針,這里的數(shù)組長度就為事件模塊的數(shù)目。其實(shí)這里的數(shù)組就是用來保存每個(gè)事件模塊的配置對象的,當(dāng)前事件模塊在所有事件模塊中的相對位置就對應(yīng)于該數(shù)組中的相對位置,這里的相對位置也即前一步中計(jì)算得到的ctx_index;

●?調(diào)用各個(gè)事件模塊的create_conf()方法創(chuàng)建各自的配置結(jié)構(gòu)體,并且將其保存在ctx指針指向的數(shù)組中;

●?調(diào)用ngx_conf_parse()方法對配置文件繼續(xù)解析,前面我們已經(jīng)講到,ngx_events_block()方法就是解析到events配置項(xiàng)的時(shí)候才調(diào)用的,因而這里的ngx_conf_parse()方法的調(diào)用就是繼續(xù)解析events配置塊的子配置項(xiàng),而該方法調(diào)用完成則說明events配置塊里的配置項(xiàng)都已經(jīng)解析完成;

●?調(diào)用各個(gè)模塊的init_conf()方法對配置項(xiàng)進(jìn)行初始化,簡單的說,就是,由于在nginx.conf中只配置了部分配置項(xiàng)的值,而剩余的配置項(xiàng)就由init_conf()方法來設(shè)置默認(rèn)值;

2. ngx_event_init_conf()—-檢查事件模塊配置結(jié)構(gòu)體是否正常創(chuàng)建

在nginx解析完nginx.conf配置文件的所有配置項(xiàng)后(包括前一步中講解的對events配置項(xiàng)的解析),其就會調(diào)用所有核心模塊的init_conf()方法對核心模塊的配置項(xiàng)進(jìn)行初始化。這里的核心模塊就包括ngx_events_module,該模塊的init_conf()方法指向的就是這里的ngx_event_init_conf()方法,該方法本質(zhì)上并沒有做什么工作,只是檢查了是否創(chuàng)建了存儲事件模塊配置項(xiàng)的結(jié)構(gòu)體數(shù)組。

如下是ngx_event_init_conf()方法的源碼:

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

上面兩個(gè)方法就是ngx_events_module核心模塊的兩個(gè)主要的配置方法,可以看到,這個(gè)核心模塊的主要作用就是創(chuàng)建了一個(gè)數(shù)組,用于存儲各個(gè)事件模塊的配置結(jié)構(gòu)體的。下面我們來看一下事件核心模塊的主要方法。

3. ngx_event_core_create_conf()—-創(chuàng)建事件核心模塊配置結(jié)構(gòu)體

在第1點(diǎn)中我們講到,解析events配置塊的子配置項(xiàng)之前,會調(diào)用各個(gè)事件模塊的create_conf()方法來創(chuàng)建其使用的存儲配置數(shù)據(jù)的結(jié)構(gòu)體,而后調(diào)用ngx_conf_parse()方法來解析子配置項(xiàng),接著調(diào)用各個(gè)事件模塊的init_conf()方法初始化各個(gè)模塊配置數(shù)據(jù)的結(jié)構(gòu)體。

這里ngx_event_core_module_ctx就是一個(gè)事件類型的模塊,其create_conf屬性指向的就是ngx_event_core_create_conf()方法,而init_conf屬性指向的就是ngx_event_core_init_conf()方法。

這一節(jié)我們首先講解ngx_event_core_create_conf()方法的實(shí)現(xiàn)原理:

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; ??} ??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; ??return?ecf; }

可以看到,這里的ngx_event_core_create_conf()方法本質(zhì)上就是創(chuàng)建了一個(gè)ngx_event_conf_t結(jié)構(gòu)體,并且將各個(gè)屬性都設(shè)置為未設(shè)置狀態(tài)。

4. ngx_event_core_init_conf()—-初始化配置結(jié)構(gòu)體

前面我們講到,在解析完各個(gè)子配置項(xiàng)之后,nginx會調(diào)用各個(gè)事件模塊的init_conf()方法,這里的核心事件模塊就是這個(gè)ngx_event_core_init_conf()方法,如下是該方法的源碼:

static?char?*?ngx_event_core_init_conf(ngx_cycle_t?*cycle,?void?*conf)?{ ??ngx_event_conf_t?*ecf?=?conf; #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) ??//?測試是否具有創(chuàng)建epoll句柄的權(quán)限 ??fd?=?epoll_create(100); ??if?(fd?!=?-1)?{ ????//?關(guān)閉創(chuàng)建的epoll句柄,并且將module指向epoll模塊 ??????(void)?close(fd); ??????module?=?&ngx_epoll_module; ??}?else?if?(ngx_errno?!=?NGX_ENOSYS)?{ ??????module?=?&ngx_epoll_module; ??} #endif ??//?這里,如果沒有前面判斷的模塊類型,則默認(rèn)使用事件模塊中的第一個(gè)模塊作為事件處理模型 ??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; ????} ??} ??//?如果此時(shí)module還是為NULL,則返回異常 ??if?(module?==?NULL)?{ ????ngx_log_error(NGX_LOG_EMERG,?cycle->log,?0,?"no?events?module?found"); ????return?NGX_CONF_ERROR; ??} ??//?下面的操作主要是判斷各個(gè)屬性是否為初始設(shè)置的無效值,如果是,則說明nginx.conf中沒有配置 ??//?關(guān)于該屬性的配置項(xiàng),那么這里就會為該屬性設(shè)置默認(rèn)值 ??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; }

ngx_event_core_init_conf()方法的主要做了兩件事:

●?選擇當(dāng)前所使用的模塊,如果沒指定,則默認(rèn)使用第一個(gè)事件模塊;

●?初始化事件核心模塊的配置結(jié)構(gòu)體的各個(gè)屬性值為默認(rèn)值。

5. ngx_event_module_init()—-核心模塊的配置項(xiàng)初始化

對于ngx_event_core_module模塊而言,其還指定了兩個(gè)方法,一個(gè)是用于初始化模塊的ngx_event_module_init()方法,另一個(gè)是用于worker進(jìn)程執(zhí)行主循環(huán)邏輯之前進(jìn)行調(diào)用的ngx_event_process_init()方法。

ngx_event_module_init()方法是在master進(jìn)程中調(diào)用的,其會在解析完nginx.conf文件中的所有配置項(xiàng)之后調(diào)用,本質(zhì)上,該方法的作用就是對當(dāng)前配置的核心模塊(事件模塊)進(jìn)行初始化。

如下是ngx_event_module_init()方法的源碼:

/** ?*?當(dāng)前方法的主要作用是申請一塊用于存儲統(tǒng)計(jì)數(shù)據(jù)的共享內(nèi)存,然后設(shè)置ngx_accept_mutex_ptr、 ?*?ngx_connection_counter、ngx_temp_number等變量的地址,如果開啟了slab?stat, ?*?那么還會設(shè)置ngx_stat_accepted、ngx_stat_handled、ngx_stat_requests等的地址,以統(tǒng)計(jì)更多的數(shù)據(jù) ?*/ 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; ??//?獲取core?event?module的配置結(jié)構(gòu)體 ??cf?=?ngx_get_conf(cycle-&gt;conf_ctx,?ngx_events_module); ??ecf?=?(*cf)[ngx_event_core_module.ctx_index]; ??if?(!ngx_test_config?&amp;&amp;?ngx_process?log,?0, ??????????????????"using?the?"%s"?event?method",?ecf-&gt;name); ??} ??//?獲取core?module的配置對象 ??ccf?=?(ngx_core_conf_t?*)?ngx_get_conf(cycle-&gt;conf_ctx,?ngx_core_module); ??ngx_timer_resolution?=?ccf-&gt;timer_resolution; #if?!(NGX_WIN32) ??{ ????ngx_int_t?limit; ????struct?rlimit?rlmt; ????if?(getrlimit(RLIMIT_NOFILE,?&amp;rlmt)?==?-1)?{ ??????ngx_log_error(NGX_LOG_ALERT,?cycle-&gt;log,?ngx_errno, ????????????????????"getrlimit(RLIMIT_NOFILE)?failed,?ignored"); ????}?else?{ ??????//?這里主要是檢查當(dāng)前事件模塊配置的connections數(shù)目是否超過了操作系統(tǒng)限制的最大文件句柄數(shù), ??????//?或者超過了配置文件中指定的最大文件句柄數(shù) ??????if?(ecf-&gt;connections?&gt;?(ngx_uint_t)?rlmt.rlim_cur ??????????&amp;&amp;?(ccf-&gt;rlimit_nofile?==?NGX_CONF_UNSET ??????????????||?ecf-&gt;connections?&gt;?(ngx_uint_t)?ccf-&gt;rlimit_nofile))?{ ????????limit?=?(ccf-&gt;rlimit_nofile?==?NGX_CONF_UNSET)?? ????????????????(ngx_int_t)?rlmt.rlim_cur?:?ccf-&gt;rlimit_nofile; ????????ngx_log_error(NGX_LOG_WARN,?cycle-&gt;log,?0, ??????????????????????"%ui?worker_connections?exceed?" ??????????????????????"open?file?resource?limit:?%i", ??????????????????????ecf-&gt;connections,?limit); ??????} ????} ??} #endif?/*?!(NGX_WIN32)?*/ ??if?(ccf-&gt;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 ??//?設(shè)置共享內(nèi)存的大小 ??shm.size?=?size; ??ngx_str_set(&amp;shm.name,?"nginx_shared_zone"); ??shm.log?=?cycle-&gt;log; ??//?為共享內(nèi)存結(jié)構(gòu)體申請內(nèi)存塊 ??if?(ngx_shm_alloc(&amp;shm)?!=?NGX_OK)?{ ????return?NGX_ERROR; ??} ??//?addr就是申請的共享內(nèi)存塊的地址 ??shared?=?shm.addr; ??ngx_accept_mutex_ptr?=?(ngx_atomic_t?*)?shared; ??ngx_accept_mutex.spin?=?(ngx_uint_t)?-1; ??if?(ngx_shmtx_create(&amp;ngx_accept_mutex,?(ngx_shmtx_sh_t?*)?shared,?cycle-&gt;lock_file.data)?!=?NGX_OK)?{ ????return?NGX_ERROR; ??} ??//?獲取ngx_connection_counter的地址 ??ngx_connection_counter?=?(ngx_atomic_t?*)?(shared?+?1?*?cl); ??//?將ngx_connection_counter的值設(shè)置為1 ??(void)?ngx_atomic_cmp_set(ngx_connection_counter,?0,?1); ??ngx_log_debug2(NGX_LOG_DEBUG_EVENT,?cycle-&gt;log,?0, ?????????????????"counter:?%p,?%uA", ?????????????????ngx_connection_counter,?*ngx_connection_counter); ??//?獲取ngx_temp_number的地址 ??ngx_temp_number?=?(ngx_atomic_t?*)?(shared?+?2?*?cl); ??tp?=?ngx_timeofday(); ??//?生成一個(gè)隨機(jī)數(shù) ??ngx_random_number?=?(tp-&gt;msec?<p>ngx_event_module_init()方法主要完成的工作有如下幾個(gè):</p><p>●?獲取配置的timer_resolution屬性值,并將其賦值給ngx_timer_resolution屬性,這個(gè)屬性的作用主要是指定更新nginx緩存的時(shí)間的定時(shí)任務(wù)的執(zhí)行時(shí)間間隔;</p><p>●?獲取nginx配置的文件描述符和當(dāng)前操作系統(tǒng)的文件描述符的配置,對比兩個(gè)值,從而更新當(dāng)前進(jìn)程所能開啟的文件描述符的個(gè)數(shù);</p><p>●?聲明一塊共享內(nèi)存,用于存儲nginx進(jìn)行統(tǒng)計(jì)用的各個(gè)屬性的數(shù)據(jù)。</p><p><strong>6. ngx_event_process_init()----初始化worker進(jìn)程</strong></p><p>ngx_event_process_init()方法主要是在worker進(jìn)程執(zhí)行主循環(huán)之前進(jìn)行初始化調(diào)用的,如下是該方法的源碼:</p><pre class="brush:php;toolbar:false">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-&gt;conf_ctx,?ngx_core_module); ??//?獲取事件核心模塊的配置對象 ??ecf?=?ngx_event_get_conf(cycle-&gt;conf_ctx,?ngx_event_core_module); ??//?判斷當(dāng)前如果滿足三個(gè)條件,則標(biāo)記當(dāng)前為使用共享鎖的方式: ??//?1.?當(dāng)前為master-worker模式; ??//?2.?當(dāng)前worker進(jìn)程的數(shù)量大于1; ??//?3.?當(dāng)前打開了使用共享鎖的開關(guān); ??if?(ccf-&gt;master?&amp;&amp;?ccf-&gt;worker_processes?&gt;?1?&amp;&amp;?ecf-&gt;accept_mutex)?{ ????ngx_use_accept_mutex?=?1; ????ngx_accept_mutex_held?=?0; ????ngx_accept_mutex_delay?=?ecf-&gt;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 ??//?這里這兩個(gè)隊(duì)列的主要作用在于,每個(gè)worker進(jìn)程在獲取到共享鎖之后,就會接收客戶端accept事件, ??//?然后將其放入到ngx_posted_accept_events隊(duì)列中,接著處理該隊(duì)列中的事件,并且將客戶端連接添加到 ??//?ngx_posted_events隊(duì)列中,然后再釋放鎖,也就是說獲取鎖的worker進(jìn)程只需要進(jìn)行accept客戶端連接, ??//?然后將鎖的權(quán)限交給其他的進(jìn)程,并且再自行處理接收到的連接的讀寫事件 ??//?創(chuàng)建ngx_posted_accept_events隊(duì)列,該隊(duì)列用于接收客戶端的連接事件 ??ngx_queue_init(&amp;ngx_posted_accept_events); ??//?創(chuàng)建ngx_posted_events隊(duì)列,該隊(duì)列用于處理客戶端連接的讀寫事件 ??ngx_queue_init(&amp;ngx_posted_events); ??//?初始化一個(gè)用于存儲事件的紅黑樹 ??if?(ngx_event_timer_init(cycle-&gt;log)?==?NGX_ERROR)?{ ????return?NGX_ERROR; ??} ??for?(m?=?0;?cycle-&gt;modules[m];?m++)?{ ????if?(cycle-&gt;modules[m]-&gt;type?!=?NGX_EVENT_MODULE)?{ ??????continue; ????} ????//?ecf-&gt;use存儲了所選用的事件模型的模塊序號,這里是找到該模塊 ????if?(cycle-&gt;modules[m]-&gt;ctx_index?!=?ecf-&gt;use)?{ ??????continue; ????} ????//?module即為所選用的事件模型對應(yīng)的模塊 ????module?=?cycle-&gt;modules[m]-&gt;ctx; ????//?調(diào)用指定事件模型的初始化方法 ????if?(module-&gt;actions.init(cycle,?ngx_timer_resolution)?!=?NGX_OK)?{ ??????/*?fatal?*/ ??????exit(2); ????} ????break; ??} #if?!(NGX_WIN32) ??//?ngx_timer_resolution表示發(fā)送更新時(shí)間事件的時(shí)間間隔 ??//?這里表示如果設(shè)置了ngx_timer_resolution,并且沒有設(shè)置定時(shí)事件。 ??//?ngx_event_flags是在事件模塊的初始化中設(shè)置的,而且只有eventport和kqueue模型才會將 ??//?NGX_USE_TIMER_EVENT設(shè)置到ngx_event_flags中 ??if?(ngx_timer_resolution?&amp;&amp;?!(ngx_event_flags?&amp;?NGX_USE_TIMER_EVENT))?{ ????struct?sigaction?sa; ????struct?itimerval?itv; ????ngx_memzero(&amp;sa,?sizeof(struct?sigaction)); ????//?這里的sa主要是添加下面的SIGALRM的信號監(jiān)聽事件,該信號的作用是每隔一段時(shí)間就會向當(dāng)前進(jìn)程發(fā)出 ????//?當(dāng)前進(jìn)程收到信號之后就會調(diào)用下面的ngx_timer_signal_handler()方法,該方法中會將 ????//?ngx_event_timer_alarm設(shè)置為1,而后當(dāng)前進(jìn)程在進(jìn)行事件循環(huán)的時(shí)候,判斷如果 ????//?ngx_event_timer_alarm為1,則會更新當(dāng)前進(jìn)程所緩存的時(shí)間數(shù)據(jù) ????sa.sa_handler?=?ngx_timer_signal_handler; ????sigemptyset(&amp;sa.sa_mask); ????//?添加SIGALRM監(jiān)聽信號 ????if?(sigaction(SIGALRM,?&amp;sa,?NULL)?==?-1)?{ ??????ngx_log_error(NGX_LOG_ALERT,?cycle-&gt;log,?ngx_errno, ????????????????????"sigaction(SIGALRM)?failed"); ??????return?NGX_ERROR; ????} ????//?設(shè)置時(shí)間間隔相關(guān)參數(shù) ????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; ????//?按照指定的時(shí)間間隔設(shè)置定時(shí)器 ????if?(setitimer(ITIMER_REAL,?&amp;itv,?NULL)?==?-1)?{ ??????ngx_log_error(NGX_LOG_ALERT,?cycle-&gt;log,?ngx_errno, ????????????????????"setitimer()?failed"); ????} ??} ??//?NGX_USE_FD_EVENT表示event?filter沒有透明數(shù)據(jù),并需要一個(gè)文件描述符表,其主要用于poll、/dev/poll ??if?(ngx_event_flags?&amp;?NGX_USE_FD_EVENT)?{ ????struct?rlimit?rlmt; ????if?(getrlimit(RLIMIT_NOFILE,?&amp;rlmt)?==?-1)?{ ??????ngx_log_error(NGX_LOG_ALERT,?cycle-&gt;log,?ngx_errno, ????????????????????"getrlimit(RLIMIT_NOFILE)?failed"); ??????return?NGX_ERROR; ????} ????//?這里主要是初始化最大個(gè)數(shù)的ngx_connection_t結(jié)構(gòu)體,將其保存在files數(shù)組中 ????cycle-&gt;files_n?=?(ngx_uint_t)?rlmt.rlim_cur; ????cycle-&gt;files?=?ngx_calloc(sizeof(ngx_connection_t?*)?*?cycle-&gt;files_n,?cycle-&gt;log); ????if?(cycle-&gt;files?==?NULL)?{ ??????return?NGX_ERROR; ????} ??} #else ??if?(ngx_timer_resolution?&amp;&amp;?!(ngx_event_flags?&amp;?NGX_USE_TIMER_EVENT))?{ ??????ngx_log_error(NGX_LOG_WARN,?cycle-&gt;log,?0, ????????????????????"the?"timer_resolution"?directive?is?not?supported?" ????????????????????"with?the?configured?event?method,?ignored"); ??????ngx_timer_resolution?=?0; ??} #endif ??//?申請指定個(gè)數(shù)的ngx_connection_t數(shù)組,這里的connection_n對應(yīng)的是配置 ??//?文件中的worker_connections所指定的大小 ??cycle-&gt;connections?=?ngx_alloc(sizeof(ngx_connection_t)?*?cycle-&gt;connection_n,?cycle-&gt;log); ??if?(cycle-&gt;connections?==?NULL)?{ ????return?NGX_ERROR; ??} ??c?=?cycle-&gt;connections; ??//?申請指定個(gè)數(shù)的ngx_event_t數(shù)組,其長度與connections數(shù)組一致, ??//?這樣便可以將connections數(shù)組與read_events數(shù)組進(jìn)行對應(yīng) ??cycle-&gt;read_events?=?ngx_alloc(sizeof(ngx_event_t)?*?cycle-&gt;connection_n,?cycle-&gt;log); ??if?(cycle-&gt;read_events?==?NULL)?{ ????return?NGX_ERROR; ??} ??rev?=?cycle-&gt;read_events; ??for?(i?=?0;?i?connection_n;?i++)?{ ????rev[i].closed?=?1;??//?初始狀態(tài)默認(rèn)讀事件都是closed狀態(tài) ????rev[i].instance?=?1;??//?初始時(shí)初始化instance為1 ??} ??//?申請指定個(gè)數(shù)的ngx_event_t數(shù)組,其長度與connections數(shù)組一致, ??//?這樣便可以將connections數(shù)組與write_events數(shù)組進(jìn)行對應(yīng) ??cycle-&gt;write_events?=?ngx_alloc(sizeof(ngx_event_t)?*?cycle-&gt;connection_n,?cycle-&gt;log); ??if?(cycle-&gt;write_events?==?NULL)?{ ????return?NGX_ERROR; ??} ??wev?=?cycle-&gt;write_events; ??for?(i?=?0;?i?connection_n;?i++)?{ ????wev[i].closed?=?1;??//?初始時(shí)寫事件默認(rèn)也都是closed狀態(tài) ??} ??i?=?cycle-&gt;connection_n; ??next?=?NULL; ??do?{ ????i--; ????//?將read_events和write_events數(shù)組的元素依次賦值到connections數(shù)組元素的read和write屬性中, ????//?并且將connections數(shù)組組裝成一個(gè)單鏈表 ????c[i].data?=?next; ????c[i].read?=?&amp;cycle-&gt;read_events[i]; ????c[i].write?=?&amp;cycle-&gt;write_events[i]; ????c[i].fd?=?(ngx_socket_t)?-1; ????next?=?&amp;c[i]; ??}?while?(i); ??//?初始狀態(tài)時(shí),所有的connections都未被使用,因而需要存儲在free_connections鏈表中 ??cycle-&gt;free_connections?=?next; ??cycle-&gt;free_connection_n?=?cycle-&gt;connection_n; ??/*?for?each?listening?socket?*/ ??ls?=?cycle-&gt;listening.elts; ??for?(i?=?0;?i?listening.nelts;?i++)?{ #if?(NGX_HAVE_REUSEPORT) ????if?(ls[i].reuseport?&amp;&amp;?ls[i].worker?!=?ngx_worker)?{ ??????continue; ????} #endif ????//?這里是為當(dāng)前所監(jiān)聽的每一個(gè)端口都綁定一個(gè)ngx_connection_t結(jié)構(gòu)體 ????c?=?ngx_get_connection(ls[i].fd,?cycle-&gt;log); ????if?(c?==?NULL)?{ ??????return?NGX_ERROR; ????} ????c-&gt;type?=?ls[i].type; ????c-&gt;log?=?&amp;ls[i].log; ????c-&gt;listening?=?&amp;ls[i]; ????ls[i].connection?=?c; ????rev?=?c-&gt;read; ????rev-&gt;log?=?c-&gt;log; ????//?標(biāo)記accept為1,表示當(dāng)前可以接收客戶端的連接事件 ????rev-&gt;accept?=?1; #if?(NGX_HAVE_DEFERRED_ACCEPT) ????rev-&gt;deferred_accept?=?ls[i].deferred_accept; #endif ????if?(!(ngx_event_flags?&amp;?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-&gt;connection; ????????if?(ngx_del_event(old-&gt;read,?NGX_READ_EVENT,?NGX_CLOSE_EVENT)?==?NGX_ERROR)?{ ??????????return?NGX_ERROR; ????????} ????????old-&gt;fd?=?(ngx_socket_t)?-1; ??????} ????} #if?(NGX_WIN32) ????if?(ngx_event_flags?&amp;?NGX_USE_IOCP_EVENT)?{ ????????ngx_iocp_conf_t??*iocpcf; ????????rev-&gt;handler?=?ngx_event_acceptex; ????????if?(ngx_use_accept_mutex)?{ ????????????continue; ????????} ????????if?(ngx_add_event(rev,?0,?NGX_IOCP_ACCEPT)?==?NGX_ERROR)?{ ????????????return?NGX_ERROR; ????????} ????????ls[i].log.handler?=?ngx_acceptex_log_error; ????????iocpcf?=?ngx_event_get_conf(cycle-&gt;conf_ctx,?ngx_iocp_module); ????????if?(ngx_event_post_acceptex(&amp;ls[i],?iocpcf-&gt;post_acceptex) ????????????==?NGX_ERROR) ????????{ ????????????return?NGX_ERROR; ????????} ????}?else?{ ????????rev-&gt;handler?=?ngx_event_accept; ????????if?(ngx_use_accept_mutex)?{ ????????????continue; ????????} ????????if?(ngx_add_event(rev,?NGX_READ_EVENT,?0)?==?NGX_ERROR)?{ ????????????return?NGX_ERROR; ????????} ????} #else ????//?SOCK_STREAM表示TCP,一般都是TCP,也就是說在接收到客戶端的accept事件之后, ????//?就會調(diào)用ngx_event_accept()方法處理該事件 ????rev-&gt;handler?=?(c-&gt;type?==?SOCK_STREAM)???ngx_event_accept ????????????????????????????????????????????:?ngx_event_recvmsg; #if?(NGX_HAVE_REUSEPORT) ????//?添加當(dāng)前事件到事件監(jiān)聽隊(duì)列中 ????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) ????if?((ngx_event_flags?&amp;?NGX_USE_EPOLL_EVENT) ????????&amp;&amp;?ccf-&gt;worker_processes?&gt;?1) ????{ ????????if?(ngx_add_event(rev,?NGX_READ_EVENT,?NGX_EXCLUSIVE_EVENT) ????????????==?NGX_ERROR) ????????{ ????????????return?NGX_ERROR; ????????} ????????continue; ????} #endif ????//?添加當(dāng)前事件到事件監(jiān)聽隊(duì)列中 ????if?(ngx_add_event(rev,?NGX_READ_EVENT,?0)?==?NGX_ERROR)?{ ??????return?NGX_ERROR; ????} #endif ??} ??return?NGX_OK; }

這里ngx_event_process_init()方法主要完成了如下幾個(gè)工作:

●?根據(jù)所使用的進(jìn)程模式和worker進(jìn)程數(shù)量,配置是否使用共享鎖的字段ngx_use_accept_mutex;

●?初始化用于處理事件的ngx_posted_accept_events隊(duì)列和ngx_posted_events隊(duì)列;

●?調(diào)用當(dāng)前所使用的事件模型模塊的init()方法,比如epoll模型在該init()方法中就會構(gòu)建一個(gè)epoll句柄,以便后續(xù)往其中添加需要監(jiān)聽的事件;

●?判斷是否配置了ngx_timer_resolution屬性,也即上一節(jié)中獲取的更新nginx緩存時(shí)間的定時(shí)任務(wù)的執(zhí)行頻率字段,如果配置了,則創(chuàng)建一個(gè)定時(shí)任務(wù)以定時(shí)設(shè)置ngx_event_timer_alarm屬性值;

●?創(chuàng)建長度相同的connections、read_events和write_events數(shù)組,并且將connections數(shù)組中每個(gè)ngx_connection_t結(jié)構(gòu)體的read屬性指向read_events數(shù)組中對應(yīng)位置的讀事件結(jié)構(gòu)體,將write屬性指向write_events數(shù)組中對應(yīng)位置的寫事件結(jié)構(gòu)體,并且將所有的connections組織成單鏈表存儲到ngx_cycle_t的free_connections屬性中;

●?為當(dāng)前nginx監(jiān)聽的各個(gè)端口配置一個(gè)ngx_connection_t結(jié)構(gòu)體,并且為其添加對應(yīng)的事件監(jiān)聽器,以等待客戶端連接的到來。

6. 小結(jié)

本文主要講解了nginx事件模塊的初始化方式,以初始化過程中各個(gè)方法的調(diào)用順序依次講解了每個(gè)方法的實(shí)現(xiàn)原理,以及其在整個(gè)流程中所起到的作用。

推薦教程:關(guān)于nginx事件模塊結(jié)構(gòu)體的詳解

以上就是講解

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊11 分享