Reprinted statement: This article can be reproduced at will, but the original address must be specified. Thank you!
The high performance of nginx should be an event-driven credit. The relevant code for nginx event processing is located in the src/event directory, and the event driver is the core of nginx, so the amount of code is relatively large. The event-driven initialization process consists of three steps.
Step 1: parse configuration file Initialization
During nginx startup initialization, ngx_conf_parse () will be called to parse the configuration file. This process will encounter configuration items similar to the following:
Events {
Worker_connections 20480;
}
The events here is a block command, and many other commands can be configured under it, such as worker_connections here. The commands that can be configured under events are defined in the ngx_event_core_commands array (in the src/event/ngx_event.c file. Each Command has its own callback function. The callback function of the events command is ngx_events_block () (located in the src/events/ngx_event.c file ), this callback function is called when the configuration file is parsed to the events command. The approximate analysis of this function is as follows:
Static char * <br/> ngx_events_block (ngx_conf_t * Cf, ngx_command_t * cmd, void * conf) <br/>{< br/> char * RV; <br/> void *** CTX; <br/> ngx_uint_t I; <br/> ngx_conf_t PCF; <br/> ngx_event_module_t * m; <br/>/* The English comments in the source code are clear. */<Br/>/* count the number of the event modules and set up their indices */<br/> ngx_event_max_module = 0; <br/> for (I = 0; ngx_modules [I]; I ++) {<br/> If (ngx_modules [I]-> type! = Ngx_event_module) {<br/> continue; <br/>}< br/> ngx_modules [I]-> ctx_index = ngx_event_max_module ++; <br/>}< br/> CTX = ngx_pcalloc (CF-> pool, sizeof (void *); <br/> If (CTX = NULL) {<br/> return ngx_conf_error; <br/>}< br/>/* assigns a pointer to each event module to save the address of the corresponding configuration structure. */<Br/> * CTX = ngx_pcalloc (CF-> pool, ngx_event_max_module * sizeof (void *); <br/> If (* CTX = NULL) {<br/> return ngx_conf_error; <br/>}< br/> * (void **) conf = CTX; <br/>/* call the create_conf function of each event module cyclically to create the Configuration Structure */<br/> for (I = 0; ngx_modules [I]; I ++) {<br/> If (ngx_modules [I]-> type! = Ngx_event_module) {<br/> continue; <br/>}< br/> M = ngx_modules [I]-> CTX; <br/> If (m-> create_conf) {<br/> (* CTX) [ngx_modules [I]-> ctx_index] = m-> create_conf (CF-> cycle); <br/> If (* CTX) [ngx_modules [I]-> ctx_index] = NULL) {<br/> return ngx_conf_error; <br/>}< br/> PCF = * Cf; <br/> CF-> CTX = CTX; <br/> CF-> module_type = ngx_event_module; <br/> CF-> cmd_type = ngx_event_conf; <br />/* Because events is a block command, many other commands can be configured in the events field. <br/> for example, use. Now, you can parse the commands in the events block, complete initialization. */<Br/> Rv = ngx_conf_parse (CF, null); <br/> * cf = PCF; <br/> If (RV! = Ngx_conf_ OK) <br/> return RV; <br/>/* execute the init_conf function of each event module cyclically and initialize the Configuration Structure */<br/> for (I = 0; ngx_modules [I]; I ++) {<br/> If (ngx_modules [I]-> type! = Ngx_event_module) {<br/> continue; <br/>}< br/> M = ngx_modules [I]-> CTX; <br/> If (m-> init_conf) {<br/> Rv = m-> init_conf (CF-> cycle, (* CTX) [ngx_modules [I]-> ctx_index]); <br/> If (RV! = Ngx_conf_ OK) {<br/> return RV; <br/>}< br/> return ngx_conf_ OK; <br/>}
The most important process in the ngx_events_block () function is to call ngx_conf_parse (CF, null). The function of calling ngx_conf_parse () Here is to parse the events {} block in the configuration file, the callback function of all the configuration commands under it is called to complete the initialization of the parsing configuration file.
Step 2: ngx_event_module_init
At the end of nginx startup initialization process (II), there is a text like this: "execute the init_module operation of all modules. It is called initialization of the module. Looking at the source code, we found that most modules, including several ngx_core_module modules, do not have this init callback function. Which modules use this callback interface? Using the search function, I finally found a module using this callback interface, which is ngx_event_core_module. Here, we will not tangle with this unique initialization function. When analyzing event-driven functions, let's look back ."; The title ngx_event_module_init is the callback function of the ngx_event_core_module module. The ngx_event_module_init code is analyzed as follows:
Static ngx_int_t <br/> ngx_event_module_init (ngx_cycle_t * cycle) <br/>{< br/> void *** CF; <br/> u_char * shared; <br/> size_t size, Cl; <br/> ngx_shm_t SHM; <br/> ngx_time_t * TP; <br/> ngx_core_conf_t * CCF; <br/> ngx_event_conf_t * ECF; </P> <p> CF = ngx_get_conf (cycle-> conf_ctx, ngx_events_module); <br/> If (cf = NULL) {<br/> return ngx_error; <br/>}< br/>/* obtain the Configuration Structure of the ngx_event_core_module module */<br/> ECF = (* Cf) [ngx_event_core_module.ctx_index]; <br/>/* obtain the Configuration Structure of the ngx_core_module */<br/> CCF = (ngx_core_conf_t *) ngx_get_conf (cycle-> conf_ctx, ngx_core_module); <br/>/* obtain the configuration parameter of the timer_resolution command from the Configuration Structure of the ngx_core_module */<br/> ngx_timer_resolution = CCF-> timer_resolution; <br/> .................... <Br/>/* If the master process is disabled, the following steps are not required. If the master process is disabled, it is <br/> A single process. The following steps are used to create shared memory implementation locks, which are not required by a single process. <Br/> */<br/> If (CCF-> master = 0) {<br/> return ngx_ OK; <br/>}< br/>/* The accept mutex already exists and does not need to be created again */<br/> If (ngx_accept_mutex_ptr) {<br/> return ngx_ OK; <br/>}< br/>/* Cl shoshould be equal or bigger than cache line size */<br/> Cl = 128; <br/>/* a size shared memory will be created later. The shared memory will be divided into three sections, <br/> ngx_accept_mutex, ngx_connection_counter, and ngx_temp_number <br/> respectively. <Br/> */<br/> size = Cl/* ngx_accept_mutex */<br/> + Cl/* ngx_connection_counter */<br/> + Cl; /* ngx_temp_number */<br/> ..................... <Br/>/* start to create shared memory. The size is size and the name is nginx_shared_zone */<br/> SHM. size = size; <br/> SHM. name. len = sizeof ("nginx_shared_zone"); <br/> SHM. name. data = (u_char *) "nginx_shared_zone"; <br/> SHM. log = Cycle-> log; <br/>/* creates the shared memory. The starting address of the shared memory is stored in SHM. ADDR */<br/> If (ngx_shm_alloc (& SHM )! = Ngx_ OK) {<br/> return ngx_error; <br/>}< br/>/* obtain the starting address of the shared memory */<br/> shared = SHM. ADDR; </P> <p>/* The accept mutex obtains the first cl size of the shared memory */<br/> ngx_accept_mutex_ptr = (ngx_atomic_t *) shared; </P> <p>/* Create an accept mutex. </P> <p> whether the implementation dependency of accept mutex supports atomic operations. If there is an atomic operation; <br/> the obtained shared memory is used to implement the accept mutex. Otherwise, the file <br/> lock is used to implement the accept mutex. </P> <p> the accept mutex is used to avoid group alarms and achieve load balancing of worker processes. <Br/> */<br/> If (ngx_shmtx_create (& ngx_accept_mutex, shared, cycle-> lock_file.data) <br/>! = Ngx_ OK) <br/>{< br/> return ngx_error; <br/>}< br/>/* ngx_connection_counter obtains the CL size of the second segment of shared memory */<br/> ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * Cl); <br/> (void) ngx_atomic_cmp_set (ngx_connection_counter, 0, 1 ); </P> <p>/* ngx_temp_number get the CL size of the third segment of the shared memory */<br/> ngx_temp_number = (ngx_atomic_t *) (shared + 2 * Cl ); <br/> ...................... <Br/> return ngx_ OK; <br/>}
Step 3: ngx_event_process_init
In the analysis of the worker process, it is mentioned that the process initialization function customized by each module is called. The ngx_event_process_init callback function is the process initialization function customized by the ngx_event_core_commands module. Therefore, after the master process has created a worker process, the worker process first initializes the process, and then calls the ngx_event_process_init function. The ngx_event_process_init code is analyzed as follows:
Static ngx_int_t <br/> ngx_event_process_init (ngx_cycle_t * cycle) <br/>{< br/> ngx_uint_t m, I; <br/> ngx_event_t * Rev, * WEV; <br/> ngx_listening_t * ls; <br/> ngx_connection_t * C, * Next, * old; <br/> ngx_core_conf_t * CCF; <br/> ngx_event_conf_t * ECF; <br/> ngx_event_module_t * module; <br/>/* obtain the Configuration Structure of the corresponding module */<br/> CCF = (ngx_core_conf_t *) ngx_get_conf (cycle-> conf_ctx, ngx_core_module); <br/> ECF = ngx_e Vent_get_conf (cycle-> conf_ctx, ngx_event_core_module); <br/>/* When the master process is enabled, the number of worker processes is greater than 1, and accetp_mutex is configured (used by default) <br/>, to use the accept mutex. <Br/> */<br/> If (CCF-> master & CCF-> worker_processes> 1 & ECF-> accept_mutex) {<br/> ngx_use_accept_mutex = 1; /* 1 use the accept mutex, 0 do not use */<br/> ngx_accept_mutex_held = 0; /* ngx_accept_mutex_held indicates whether to obtain the accept mutex */<br/> ngx_accept_mutex_delay = ECF-> accept_mutex_delay;/* after an error occurred while obtaining the mutex, interval of next snatching */<br/>} else {<br/> ngx_use_accept_mutex = 0; <br/>}< br/>/* initialize the timer, A red-black image is created to maintain the timer. */<Br/> If (ngx_event_timer_init (cycle-> log) = ngx_error) {<br/> return ngx_error; <br/>}< br/> for (m = 0; ngx_modules [m]; m ++) {<br/> If (ngx_modules [m]-> type! = Ngx_event_module) {<br/> continue;/* Skip non-ngx_event_module */<br/>}< br/> If (ngx_modules [m]-> ctx_index! = ECF-> Use) {<br/> continue;/* skip a module that is not specified by the use configuration command, linux default epoll */<br/>}< br/> module = ngx_modules [m]-> CTX; <br/>/* calls the init function of the specific event module. </P> <p> nginx implements many event modules, such as epoll, poll, select, kqueue, AIO <br/> (these modules are located in the src/event/modules directory) and so on. Therefore, nginx abstracts the event module. <br/>, you can easily use different event models on different systems, and expand new event models. <br/>. From then on, we will focus on epoll. </P> <p> the init callback here actually calls the ngx_epoll_init function. Module-> actions structure <br/> encapsulates all epoll interface functions. Nginx registers epoll to the event abstraction layer through the actions structure. Actions is ngx_event_actions_t in src/event/ngx_event.h <br/> */<br/> If (module-> actions. INIT (cycle, ngx_timer_resolution )! = Ngx_ OK) {<br/>/* fatal */<br/> exit (2); <br/>}< br/> break;/* jump out of the loop, only a specific event model */<br/>}< br/> ..................... .... <Br/>/* create a connection array to maintain all connections. <br/> this process is already in the worker process, therefore, each worker has its own <br/> connection array. <Br/> */<br/> cycle-> connections = <br/> ngx_alloc (sizeof (ngx_connection_t) * cycle-> connection_n, cycle-> log ); <br/> If (cycle-> connections = NULL) {<br/> return ngx_error; <br/>}< br/> C = Cycle-> connections; <br/>/* Create a read event array */<br/> cycle-> read_events = ngx_alloc (sizeof (ngx_event_t) * cycle-> connection_n, <br/> cycle-> log); <br/> If (cycle-> read_events = NULL) {<br/> return ngx_error; <br/ >}< Br/> REV = Cycle-> read_events; <br/> for (I = 0; I <cycle-> connection_n; I ++) {<br/> rev [I]. closed = 1; <br/> rev [I]. instance = 1; <br/>}</P> <p>/* Create a write event array */<br/> cycle-> write_events = ngx_alloc (sizeof (ngx_event_t) * cycle-> connection_n, <br/> cycle-> log); <br/> If (cycle-> write_events = NULL) {<br/> return ngx_error; <br/>}< br/> WEV = Cycle-> write_events; <br/> for (I = 0; I <cycle-> Connection_n; I ++) {<br/> WEV [I]. closed = 1; <br/>}< br/> I = Cycle-> connection_n; <br/> next = NULL; </P> <p>/* initializes the entire connection array. The connection array is very clever. <br/> you can quickly obtain and release a connection structure. Next, draw a picture to see in detail <br/> this connection. <Br/> */<br/> do {<br/> I --; <br/> C [I]. data = next; <br/> C [I]. read = & cycle-> read_events [I]; <br/> C [I]. write = & cycle-> write_events [I]; <br/> C [I]. FD = (ngx_socket_t)-1; <br/> next = & C [I]; <br/>} while (I ); <br/> cycle-> free_connections = next; <br/> cycle-> free_connection_n = Cycle-> connection_n; <br/>/* for each listening socket */</P> <p>/* assign a connection to each listener socket from the connection array, that is, a slot */<br/> ls = Cycle-> listening. ELTs; <br/> for (I = 0; I <cycle-> listening. nelts; I ++) {<br/>/* Get a new connection slot from the connection */<br/> C = ngx_get_connection (LS [I]. FD, cycle-> log); <br/> If (C = NULL) {<br/> return ngx_error; <br/>}< br/> C-> log = & LS [I]. log; <br/> C-> listening = & LS [I]; <br/> ls [I]. connection = C; <br/> REV = C-> read; <br/> rev-> log = C-> log; <br/> rev-> Accept = 1; /* When a read event occurs, call accept */<br/> ..... ............... <Br/> # If (ngx_win32) <br/> ................... <Br/> # else <br/>/* registers the callback function ngx_event_accept */<br/> rev-> handler = ngx_event_accept; <br/>/* If accept_mutex is used, the listener socket is not put into epoll for the time being. <br/> instead, the worker waits until the accept mutex is obtained and then enters epoll, avoid the occurrence of <br/> surprise groups. <Br/> */<br/> If (ngx_use_accept_mutex) {<br/> continue; <br/>}< br/> If (ngx_event_flags & ngx_use_rtsig_event) {<br/> If (ngx_add_conn (c) = ngx_error) {<br/> return ngx_error; <br/>}< br/>} else {<br/>/* If the accept mutex is not used, put the listening socket in <br/> epoll. <Br/> */<br/> If (ngx_add_event (Rev, ngx_read_event, 0) = ngx_error) {<br/> return ngx_error; <br/>}< br/> # endif <br/>}< br/> return ngx_ OK; <br/>}
At this point, the three-step initialization process of event-driven is complete.