記事「nginx イベント モジュール構造の詳細な説明 」では、nginx イベント モジュールの全体的なワークフローを説明し、イベント モジュールを編成する各メソッドの役割に焦点を当てます。この記事では主にこのプロセス全体に焦点を当て、nginx イベント モジュールの実装の詳細をソース コードの観点から説明します。
1. ngx_events_block()
----イベント設定ブロックの解析中
nginx が解析中nginx.conf
ファイルを構成するときに、現在解析されている構成アイテムの名前が events
で、それが構成ブロックである場合、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; // 如果存储事件模块配置数据的配置项不为空,说明已经解析过配置项了,因而直接返回 if (*(void **) conf) { return "is duplicate"; } // 这里主要是计算event模块的个数,并且将各个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; } // 将数组值赋值到conf中,也即关联到核心配置对象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; // 如果当前模块的create_conf()方法不为空,则调用该方法创建存储配置项的结构体 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结构体进行了复制,临时存储在pcf中,然后初始化当前的*cf结构体的模块相关的参数, // 以进行下一步的解析 pcf = *cf; cf->ctx = ctx; cf->module_type = NGX_EVENT_MODULE; cf->cmd_type = NGX_EVENT_CONF; // 解析events{}配置块中的子配置项 rv = ngx_conf_parse(cf, NULL); // 重新将pcf复制给*cf,以供后面返回使用 *cf = pcf; if (rv != NGX_CONF_OK) { return rv; } // 到这里,说明events{}配置块的配置项都解析完成了,因而这里调用各个模块的init_conf()方法, // 进行配置项的初始化和合并工作 for (i = 0; cf->cycle->modules[i]; i++) { if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) { continue; } m = cf->cycle->modules[i]->ctx; // 如果当前模块的init_conf()不为空,则调用其init_conf()方法初始化配置项 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()
このメソッドによって完了する主なタスクは次のとおりです:
● Call ngx_count_modules()
メソッドは、イベント モジュールのシリアル番号をマークします。ここでの並べ替えは、すべてのイベント タイプ モジュール内の現在のモジュールの順序をマークすることであり、シリアル番号は次の場所に保存されることに注意してください。各モジュールの ctx_index
属性。たとえば、イベント タイプ コア モジュール ngx_event_core_module
の ctx_index
は、0
;
# はポインタ ctx
メモリ空間に適用され、配列に適用され、そのアドレスが ctx ポインタに割り当てられます。ここでの配列の長さはイベント モジュールの数です。実際、ここでの配列は、各イベント モジュールの構成オブジェクトを保存するために使用されます。すべてのイベント モジュール間の現在のイベント モジュールの相対位置は、配列内の相対位置に対応します。ここでの相対位置も、前の手順で計算されます。 . ctx_index
;
# 各イベント モジュールの create_conf()
メソッドを呼び出して独自の構成構造を作成し、それを ctx## に保存します。 # ポインター 指す配列内;
#ngx_conf_parse() メソッドを呼び出して、構成ファイルの解析を続行します。前に述べたように、
ngx_events_block()メソッドはイベント構成を解析します。これは項目が選択された場合にのみ呼び出されます。そのため、ここでの
ngx_conf_parse() メソッドの呼び出しは、イベント構成ブロックのサブ構成項目の解析を継続することです。メソッド呼び出しの完了は、
events 構成ブロックの構成項目が解析されたことを示します。
init_conf() メソッドを呼び出して、簡単に言うと、
nginx.conf# では一部の設定項目の値のみが設定され、残りの設定項目は init_conf( )
メソッド;
ngx_event_init_conf( )----イベントモジュール構成構造体が正常に作成されたか確認しますAfter nginx は、nginx.conf 設定ファイルのすべての設定項目 (前のステップの項目解析で説明したイベント設定を含む) を解析し、すべてのコア モジュールの init_conf() メソッドを呼び出して、コア モジュールの設定項目を初期化します。ここのコア モジュールには
が含まれています。このモジュールの init_conf()
メソッドは ngx_event_init_conf()
メソッドを指します。このメソッドは本質的には何も行いません。 works は、イベント モジュール構成項目を格納する構造体の配列が作成されたかどうかを確認するだけです。 以下は、
メソッドのソース コードです: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">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;
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
上記の 2 つのメソッドは、
3.
----イベント コア モジュール構成構造の作成ポイント 1 で述べたように、サブルーチンの前に解析を行います。イベント構成ブロックの構成項目。各イベント モジュールの create_conf()
ngx_conf_parse()サブ構成項目を解析し、各イベント モジュールの init_conf() メソッドを呼び出して、各モジュールの構成データの構造を初期化します。
ここで
ngx_event_core_module_ctx
ngx_event_core_create_conf()メソッドを指し、
init_conf属性は#を指します。 ##ngx_event_core_init_conf()
メソッド。 このセクションでは、まず
ngx_event_core_create_conf()
メソッドの実装原理について説明します。
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() の本質は次のとおりです。
ここでの方法 上記は、
構造体を作成し、各属性を未設定の状態に設定することです。 4.
ngx_event_core_init_conf()
前述したように、各サブ構成項目を解析した後、nginx各イベント モジュールの
init_conf() メソッドを呼び出します。ここでのコア イベント モジュールは、
メソッドです。このメソッドのソース コードは次のとおりです: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">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)
// 测试是否具有创建epoll句柄的权限
fd = epoll_create(100);
if (fd != -1) {
// 关闭创建的epoll句柄,并且将module指向epoll模块
(void) close(fd);
module = &ngx_epoll_module;
} else if (ngx_errno != NGX_ENOSYS) {
module = &ngx_epoll_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;
}
}
// 如果此时module还是为NULL,则返回异常
if (module == NULL) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
return NGX_CONF_ERROR;
}
// 下面的操作主要是判断各个属性是否为初始设置的无效值,如果是,则说明nginx.conf中没有配置
// 关于该属性的配置项,那么这里就会为该属性设置默认值
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;
}</pre><div class="contentsignin">ログイン後にコピー</div></div><p>ngx_event_core_init_conf()方法的主要做了两件事:</p><p>● 选择当前所使用的模块,如果没指定,则默认使用第一个事件模块;</p><p>● 初始化事件核心模块的配置结构体的各个属性值为默认值。</p><p><strong>5. <code>ngx_event_module_init()
----核心模块的配置项初始化
对于ngx_event_core_module
模块而言,其还指定了两个方法,一个是用于初始化模块的ngx_event_module_init
()
方法,另一个是用于worker进程执行主循环逻辑之前进行调用的ngx_event_process_init()
方法。
ngx_event_module_init()
方法是在master进程中调用的,其会在解析完nginx.conf文件中的所有配置项之后调用,本质上,该方法的作用就是对当前配置的核心模块(事件模块)进行初始化。
如下是ngx_event_module_init()
方法的源码:
/** * 当前方法的主要作用是申请一块用于存储统计数据的共享内存,然后设置ngx_accept_mutex_ptr、 * ngx_connection_counter、ngx_temp_number等变量的地址,如果开启了slab stat, * 那么还会设置ngx_stat_accepted、ngx_stat_handled、ngx_stat_requests等的地址,以统计更多的数据 */ 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的配置结构体 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); } // 获取core module的配置对象 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 { // 这里主要是检查当前事件模块配置的connections数目是否超过了操作系统限制的最大文件句柄数, // 或者超过了配置文件中指定的最大文件句柄数 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 // 设置共享内存的大小 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; } // addr就是申请的共享内存块的地址 shared = shm.addr; ngx_accept_mutex_ptr = (ngx_atomic_t *) shared; ngx_accept_mutex.spin = (ngx_uint_t) -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_connection_counter = (ngx_atomic_t *) (shared + 1 * cl); // 将ngx_connection_counter的值设置为1 (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_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; }
ngx_event_module_init()
方法主要完成的工作有如下几个:
● 获取配置的timer_resolution
属性值,并将其赋值给ngx_timer_resolution
属性,这个属性的作用主要是指定更新nginx缓存的时间的定时任务的执行时间间隔;
● 获取nginx配置的文件描述符和当前操作系统的文件描述符的配置,对比两个值,从而更新当前进程所能开启的文件描述符的个数;
● 声明一块共享内存,用于存储nginx进行统计用的各个属性的数据。
6. ngx_event_process_init()
----初始化worker进程
ngx_event_process_init()
方法主要是在worker进程执行主循环之前进行初始化调用的,如下是该方法的源码:
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); // 判断当前如果满足三个条件,则标记当前为使用共享锁的方式: // 1. 当前为master-worker模式; // 2. 当前worker进程的数量大于1; // 3. 当前打开了使用共享锁的开关; 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 // 这里这两个队列的主要作用在于,每个worker进程在获取到共享锁之后,就会接收客户端accept事件, // 然后将其放入到ngx_posted_accept_events队列中,接着处理该队列中的事件,并且将客户端连接添加到 // ngx_posted_events队列中,然后再释放锁,也就是说获取锁的worker进程只需要进行accept客户端连接, // 然后将锁的权限交给其他的进程,并且再自行处理接收到的连接的读写事件 // 创建ngx_posted_accept_events队列,该队列用于接收客户端的连接事件 ngx_queue_init(&ngx_posted_accept_events); // 创建ngx_posted_events队列,该队列用于处理客户端连接的读写事件 ngx_queue_init(&ngx_posted_events); // 初始化一个用于存储事件的红黑树 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; } // ecf->use存储了所选用的事件模型的模块序号,这里是找到该模块 if (cycle->modules[m]->ctx_index != ecf->use) { continue; } // module即为所选用的事件模型对应的模块 module = cycle->modules[m]->ctx; // 调用指定事件模型的初始化方法 if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) { /* fatal */ exit(2); } break; } #if !(NGX_WIN32) // ngx_timer_resolution表示发送更新时间事件的时间间隔 // 这里表示如果设置了ngx_timer_resolution,并且没有设置定时事件。 // ngx_event_flags是在事件模块的初始化中设置的,而且只有eventport和kqueue模型才会将 // NGX_USE_TIMER_EVENT设置到ngx_event_flags中 if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) { struct sigaction sa; struct itimerval itv; ngx_memzero(&sa, sizeof(struct sigaction)); // 这里的sa主要是添加下面的SIGALRM的信号监听事件,该信号的作用是每隔一段时间就会向当前进程发出 // 当前进程收到信号之后就会调用下面的ngx_timer_signal_handler()方法,该方法中会将 // ngx_event_timer_alarm设置为1,而后当前进程在进行事件循环的时候,判断如果 // ngx_event_timer_alarm为1,则会更新当前进程所缓存的时间数据 sa.sa_handler = ngx_timer_signal_handler; sigemptyset(&sa.sa_mask); // 添加SIGALRM监听信号 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; // 按照指定的时间间隔设置定时器 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setitimer() failed"); } } // NGX_USE_FD_EVENT表示event filter没有透明数据,并需要一个文件描述符表,其主要用于poll、/dev/poll 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; } // 这里主要是初始化最大个数的ngx_connection_t结构体,将其保存在files数组中 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; } } #else if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) { ngx_log_error(NGX_LOG_WARN, cycle->log, 0, "the \"timer_resolution\" directive is not supported " "with the configured event method, ignored"); ngx_timer_resolution = 0; } #endif // 申请指定个数的ngx_connection_t数组,这里的connection_n对应的是配置 // 文件中的worker_connections所指定的大小 cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log); if (cycle->connections == NULL) { return NGX_ERROR; } c = cycle->connections; // 申请指定个数的ngx_event_t数组,其长度与connections数组一致, // 这样便可以将connections数组与read_events数组进行对应 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; // 初始状态默认读事件都是closed状态 rev[i].instance = 1; // 初始时初始化instance为1 } // 申请指定个数的ngx_event_t数组,其长度与connections数组一致, // 这样便可以将connections数组与write_events数组进行对应 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; // 初始时写事件默认也都是closed状态 } i = cycle->connection_n; next = NULL; do { i--; // 将read_events和write_events数组的元素依次赋值到connections数组元素的read和write属性中, // 并且将connections数组组装成一个单链表 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); // 初始状态时,所有的connections都未被使用,因而需要存储在free_connections链表中 cycle->free_connections = next; cycle->free_connection_n = cycle->connection_n; /* for each listening socket */ 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 // 这里是为当前所监听的每一个端口都绑定一个ngx_connection_t结构体 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; // 标记accept为1,表示当前可以接收客户端的连接事件 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) if (ngx_event_flags & NGX_USE_IOCP_EVENT) { ngx_iocp_conf_t *iocpcf; rev->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->conf_ctx, ngx_iocp_module); if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex) == NGX_ERROR) { return NGX_ERROR; } } else { rev->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事件之后, // 就会调用ngx_event_accept()方法处理该事件 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) if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ccf->worker_processes > 1) { if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT) == NGX_ERROR) { return NGX_ERROR; } continue; } #endif // 添加当前事件到事件监听队列中 if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } #endif } return NGX_OK; }
这里ngx_event_process_init()
方法主要完成了如下几个工作:
● 根据所使用的进程模式和worker进程数量,配置是否使用共享锁的字段ngx_use_accept_mutex
;
● 初始化用于处理事件的ngx_posted_accept_events
队列和ngx_posted_events
队列;
● 调用当前所使用的事件模型模块的init()方法,比如epoll模型在该init()方法中就会构建一个epoll句柄,以便后续往其中添加需要监听的事件;
● 判断是否配置了ngx_timer_resolution
属性,也即上一节中获取的更新nginx缓存时间的定时任务的执行频率字段,如果配置了,则创建一个定时任务以定时设置ngx_event_timer_alarm
属性值;
● 创建长度相同的connections
、read_events
和write_events
数组,并且将connections
数组中每个ngx_connection_t
结构体的read属性指向read_events
数组中对应位置的读事件结构体,将write属性指向write_events
数组中对应位置的写事件结构体,并且将所有的connections
组织成单链表存储到ngx_cycle_t
的free_connections
属性中;
● 为当前nginx监听的各个端口配置一个ngx_connection_t
结构体,并且为其添加对应的事件监听器,以等待客户端连接的到来。
6. 小结
本文主要讲解了nginx事件模块的初始化方式,以初始化过程中各个方法的调用顺序依次讲解了每个方法的实现原理,以及其在整个流程中所起到的作用。
推荐教程:Nginx教程
以上がnginxイベントモジュールの実装詳細を説明するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。