nginx 이벤트 모듈의 구현 세부 사항 설명

藏色散人
풀어 주다: 2020-01-21 17:28:58
앞으로
2811명이 탐색했습니다.

"nginx 이벤트 모듈 구조에 대한 자세한 설명" 기사에서는 nginx 이벤트 모듈의 전반적인 작업 흐름을 설명하고, 이벤트 모듈을 구성하는 각 방법의 역할에 중점을 둡니다. 이 기사에서는 주로 이 전체 프로세스에 중점을 둡니다. , 소스 코드 관점에서 nginx 이벤트 모듈의 구현 세부 사항을 설명합니다.

nginx 이벤트 모듈의 구현 세부 사항 설명

1.ngx_events_block()----이벤트 구성 블록 구문 분석ngx_events_block()----events配置块解析

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()方法主要完成的工作有如下几个:

● 调用ngx_count_modules()方法对事件模块序号进行标记,需要注意的是,这里的排序是针对当前模块在所有事件类型模块中的顺序进行标记,并且将序号保存在各模块的ctx_index属性中,比如这里的事件类型核心模块ngx_event_core_modulectx_index就为0

● 为指针ctx申请内存空间,并且申请一个数组,将其地址赋值给ctx指针,这里的数组长度就为事件模块的数目。其实这里的数组就是用来保存每个事件模块的配置对象的,当前事件模块在所有事件模块中的相对位置就对应于该数组中的相对位置,这里的相对位置也即前一步中计算得到的ctx_index

● 调用各个事件模块的create_conf()方法创建各自的配置结构体,并且将其保存在ctx指针指向的数组中;

● 调用ngx_conf_parse()方法对配置文件继续解析,前面我们已经讲到,ngx_events_block()方法就是解析到events配置项的时候才调用的,因而这里的ngx_conf_parse()方法的调用就是继续解析events配置块的子配置项,而该方法调用完成则说明events配置块里的配置项都已经解析完成;

● 调用各个模块的init_conf()方法对配置项进行初始化,简单的说,就是,由于在nginx.conf中只配置了部分配置项的值,而剩余的配置项就由init_conf()方法来设置默认值;

2.ngx_event_init_conf()----检查事件模块配置结构体是否正常创建

在nginx解析完nginx.conf配置文件的所有配置项后(包括前一步中讲解的对events配置项的解析),其就会调用所有核心模块的init_conf()方法对核心模块的配置项进行初始化。这里的核心模块就包括ngx_events_module,该模块的init_conf()方法指向的就是这里的ngx_event_init_conf()方法,该方法本质上并没有做什么工作,只是检查了是否创建了存储事件模块配置项的结构体数组。

如下是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; }
로그인 후 복사

上面两个方法就是ngx_events_module核心模块的两个主要的配置方法,可以看到,这个核心模块的主要作用就是创建了一个数组,用于存储各个事件模块的配置结构体的。下面我们来看一下事件核心模块的主要方法。

3.ngx_event_core_create_conf()----创建事件核心模块配置结构体

在第1点中我们讲到,解析events配置块的子配置项之前,会调用各个事件模块的create_conf()方法来创建其使用的存储配置数据的结构体,而后调用ngx_conf_parse()方法来解析子配置项,接着调用各个事件模块的init_conf()方法初始化各个模块配置数据的结构体。

这里ngx_event_core_module_ctx就是一个事件类型的模块,其create_conf属性指向的就是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()方法本质上就是创建了一个ngx_event_conf_t结构体,并且将各个属性都设置为未设置状态。

4.ngx_event_core_init_conf()----初始化配置结构体

前面我们讲到,在解析完各个子配置项之后,nginx会调用各个事件模块的init_conf()方法,这里的核心事件模块就是这个ngx_event_core_init_conf()

nginx가 nginx.conf구성 파일을 구문 분석할 때, 현재 파싱된 구성 항목의 이름이 events이고 구성 블록인 경우 ngx_events_block()메서드가 호출되어 구성 블록을 파싱합니다. 이 메서드의 소스 코드:
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; }
로그인 후 복사
로그인 후 복사
ngx_events_block()메서드는 주로 다음 작업을 완료합니다. ● ngx_count_modules()메서드를 호출하여 이벤트 모듈 일련 번호를 표시합니다. 여기서 정렬은 모든 이벤트 유형 모듈에서 현재 모듈의 순서를 표시하고 이벤트 유형 코어 모듈과 같은 각 모듈의 ctx_index속성에 시퀀스 번호를 저장하는 것입니다. 여기서 ngx_event_core_module ctx_index0입니다. ● 포인터 ctx에 대한 메모리 공간을 신청하세요. 배열을 만들고 해당 주소를 ctx 포인터에 할당합니다. 여기서 배열의 길이는 이벤트 모듈의 수입니다. 실제로 여기의 배열은 각 이벤트 모듈의 구성 개체를 저장하는 데 사용됩니다. 모든 이벤트 모듈 중 현재 이벤트 모듈의 상대 위치는 배열의 상대 위치도 이전 단계에서 계산됩니다. . ctx_index; ● 각 이벤트 모듈의 create_conf()메서드를 호출하여 자체 구성 구조를 만들고 ctx가 가리키는 포인터에 저장합니다.배열에서 가 호출되므로 여기서 ngx_conf_parse()메서드 호출은 이벤트 구성 블록의 하위 구성 항목을 계속 구문 분석하고 메서드 호출을 완료하는 것입니다. events구성 블록이 구문 분석되었음을 의미합니다. ● 각 모듈의 init_conf()메서드를 호출하여 구성 항목을 초기화합니다. nginx.conf에서는 일부 구성 항목의 값만 구성되고 나머지 구성 항목은 init_conf()메서드에 의해 기본값으로 설정되기 때문입니다. 2. ngx_event_init_conf()--- - 이벤트 모듈 구성 구조가 정상적으로 생성되었는지 확인nginx가 nginx.conf 구성 파일의 모든 구성 항목을 구문 분석한 후( 이전 단계에서 설명한 이벤트 구성 항목의 구문 분석을 포함하여 모든 항목을 호출합니다. 핵심 모듈의 init_conf() 메서드는 핵심 모듈의 구성 항목을 초기화합니다. 여기서 핵심 모듈에는 ngx_events_module이 포함되어 있습니다. 이 모듈의 init_conf()메서드는 여기서 ngx_event_init_conf()메서드를 가리킵니다. 별다른 작업은 하지 않았고, 이벤트 모듈 구성 항목을 저장하는 구조 배열이 생성되었는지 확인했습니다. 다음은 ngx_event_init_conf()메서드의 소스 코드입니다.
/** * 当前方法的主要作用是申请一块用于存储统计数据的共享内存,然后设置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_events_module핵심 모듈의 두 가지 주요 구성 메서드입니다. 이 코어를 볼 수 있습니다. 모듈의 주요 기능은 각 이벤트 모듈의 구성 구조를 저장하는 배열을 만드는 것입니다. 이벤트 코어 모듈의 주요 메소드를 살펴보겠습니다. 3. ngx_event_core_create_conf()----이벤트 코어 모듈 구성 구조 생성1번에서 언급했듯이 이벤트 구성의 하위 구성 항목을 구문 분석하기 전에 block에서 각 이벤트 모듈의 create_conf()메서드가 호출되어 구성 데이터를 저장하는 데 사용되는 구조를 생성한 다음 ngx_conf_parse()메서드를 호출하여 구문 분석합니다. 하위 구성 항목을 구성한 후 각 이벤트 모듈의 init_conf() 메서드를 호출하여 각 모듈의 구성 데이터 구조를 초기화합니다. 여기서 ngx_event_core_module_ctx는 이벤트 유형 모듈이고, 해당 create_conf 속성은 ngx_event_core_create_conf()메서드를 가리키며, init_conf속성은 < code를 가리킵니다. >ngx_event_core_init_conf()메소드. 이 섹션에서는 먼저 ngx_event_core_create_conf()메서드의 구현 원리를 설명합니다.
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_core_create_conf()메서드는 기본적으로 < code>ngx_event_conf_t구조를 구성하고 각 속성을 설정되지 않은 상태로 설정합니다. 4. ngx_event_core_init_conf()----초기화 구성 구조앞서 언급했듯이 각 하위 구성 항목을 구문 분석한 후 nginx는 각 이벤트 모듈 를 호출합니다. init_conf()메소드, 여기서 핵심 이벤트 모듈은 이 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) // 测试是否具有创建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; }
로그인 후 복사
로그인 후 복사

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

● 选择当前所使用的模块,如果没指定,则默认使用第一个事件模块;

● 初始化事件核心模块的配置结构体的各个属性值为默认值。

5.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属性值;

● 创建长度相同的connectionsread_eventswrite_events数组,并且将connections数组中每个ngx_connection_t结构体的read属性指向read_events数组中对应位置的读事件结构体,将write属性指向write_events数组中对应位置的写事件结构体,并且将所有的connections组织成单链表存储到ngx_cycle_tfree_connections属性中;

● 为当前nginx监听的各个端口配置一个ngx_connection_t结构体,并且为其添加对应的事件监听器,以等待客户端连接的到来。

6. 小结

本文主要讲解了nginx事件模块的初始化方式,以初始化过程中各个方法的调用顺序依次讲解了每个方法的实现原理,以及其在整个流程中所起到的作用。

推荐教程:Nginx教程

위 내용은 nginx 이벤트 모듈의 구현 세부 사항 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:oschina.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!