|----------(ngx_worker_process_cycle->ngx_worker_process_init)

                              |--------->for(;;) {ngx_process_events_and_timers()}

ngx_start_worker_processes---| ngx_processes[]相关的操作赋值流程
              |----------ngx_pass_open_channel

1、ngx_worker_process_init 工作进程初始化的时候,调用init_process的回调函数,其回调函数为ngx_event_process_init

ngx_event_process_init 实现:

/*
|----------(ngx_worker_process_cycle->ngx_worker_process_init)
ngx_start_worker_processes---| ngx_processes[]相关的操作赋值流程
|----------ngx_pass_open_channel
*/
static void
ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
{ //主要工作是把CPU和进程绑定 创建epoll_crate等
sigset_t set;
uint64_t cpu_affinity;
ngx_int_t n;
ngx_uint_t i;
struct rlimit rlmt;
ngx_core_conf_t *ccf;
ngx_listening_t *ls; if (ngx_set_environment(cycle, NULL) == NULL) {
/* fatal */
exit(2);
} ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); if (worker >= 0 && ccf->priority != 0) { /*设置优先级*/
if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setpriority(%d) failed", ccf->priority);
}
} if (ccf->rlimit_nofile != NGX_CONF_UNSET) {
rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;
rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; //RLIMIT_NOFILE指定此进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误。
if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setrlimit(RLIMIT_NOFILE, %i) failed",
ccf->rlimit_nofile);
}
} if (ccf->rlimit_core != NGX_CONF_UNSET) {
rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;
rlmt.rlim_max = (rlim_t) ccf->rlimit_core;
//修改工作进程的core文件尺寸的最大值限制(RLIMIT_CORE),用于在不重启主进程的情况下增大该限制。
if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setrlimit(RLIMIT_CORE, %O) failed",
ccf->rlimit_core);
}
ngx_log_debugall(cycle->log, 0, "setrlimit(RLIMIT_CORE, &rlmt) OK,rlimit_core:%O",ccf->rlimit_core);
} if (geteuid() == 0) {
if (setgid(ccf->group) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"setgid(%d) failed", ccf->group);
/* fatal */
exit(2);
} if (initgroups(ccf->username, ccf->group) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"initgroups(%s, %d) failed",
ccf->username, ccf->group);
} if (setuid(ccf->user) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"setuid(%d) failed", ccf->user);
/* fatal */
exit(2);
}
} if (worker >= 0) {
cpu_affinity = ngx_get_cpu_affinity(worker); if (cpu_affinity) {
ngx_setaffinity(cpu_affinity, cycle->log);
}
} #if (NGX_HAVE_PR_SET_DUMPABLE) /* allow coredump after setuid() in Linux 2.4.x */ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"prctl(PR_SET_DUMPABLE) failed");
} #endif if (ccf->working_directory.len) { //路径必须存在,否则返回错误
if (chdir((char *) ccf->working_directory.data) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"chdir(\"%s\") failed", ccf->working_directory.data);
/* fatal */
exit(2);
}
ngx_log_debugall(cycle->log, 0, "chdir %V OK", &ccf->working_directory);
} sigemptyset(&set); if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
} srandom((ngx_pid << 16) ^ ngx_time()); /*
* disable deleting previous events for the listening sockets because
* in the worker processes there are no events at all at this point
*/
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
ls[i].previous = NULL;
} for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->init_process) {
if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { //ngx_event_process_init等
/* fatal */
exit(2);
}
}
} /* 用socketpair生成两个sock[0]和sock[1]用于父进程和子进程的通信,当父进程使用其中一个socket时,为什么要调用close,关闭子进程的sock[1],代码如下:
int r = socketpair( AF_UNIX, SOCK_STREAM, 0, fd );
if ( fork() ) {
Parent process: echo client
int val = 0;
close( fd[1] );
while ( 1 ) {
sleep( 1 );
++val;
printf( "Sending data: %d\n", val );
write( fd[0], &val, sizeof(val) );
read( fd[0], &val, sizeof(val) );
printf( "Data received: %d\n", val );
}
}
else {
Child process: echo server
int val;
close( fd[0] );
while ( 1 ) {
read( fd[1], &val, sizeof(val) );
++val;
write( fd[1], &val, sizeof(val) );
}
}
}
转载出处:http://blog.csdn.net/sunnyboychina/archive/2007/11/14/1884076.aspx
调用socketpair创建的两个socket都是打开的,fork后子进程会继承这两个打开的socket。为了实现父子进程通过socket pair(类似于管道)通信,必须保证父子进程分别open某一个socket。 channel[0] 是用来发送信息的,channel[1]是用来接收信息的。那么对自己而言,它需要向其他进程发送信息,需要保留其它进程的channel[0],
关闭channel[1]; 对自己而言,则需要关闭channel[0]。 最后把ngx_channel放到epoll中,从第一部分中的介绍我们可以知道,这个ngx_channel
实际就是自己的 channel[1]。这样有信息进来的时候就可以通知到了。
*/
//关闭所有其它子进程对应的 channel[1] 和 自己的 channel[0]。从而实现子进程的channel[1]和主进程的channel[0]通信
for (n = 0; n < ngx_last_process; n++) { if (ngx_processes[n].pid == -1) {
continue;
} if (n == ngx_process_slot) {
continue;
} if (ngx_processes[n].channel[1] == -1) {
continue;
} if (close(ngx_processes[n].channel[1]) == -1) { //关闭除本进程以外的其他所有进程的读端
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() channel failed");
}
} if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { //关闭本进程的写端 ,剩下的一条通道还是全双工的
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() channel failed");
} #if 0
ngx_last_process = 0;
#endif //调用epoll add 把ngx_chanel 加入epoll 中
if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
ngx_channel_handler) //在ngx_spawn_process中赋值
== NGX_ERROR)
{
/* fatal */
exit(2);
}
}

module->actions.init 主要是调用epoll/kqueque等模型模块的init初始化方法。epoll调用是ngx_epoll_init这个方法。

ecf->use在配置文件中,我们配置了:“use epoll;”。但是这里存储的是 epoll/kqueue等模块是索引ID。通过索引ID就可以快速在cycle->modules找到对应的模块。

CP连接和读取事件逻辑:

在 Nginx 的初始化启动过程中,worker 工作进程会调用事件模块的ngx_event_process_init 方法为每个监听套接字ngx_listening_t 分配一个 ngx_connection_t 连接,

并设置该连接上读事件的回调方法handler 为ngx_event_accept,同时将读事件挂载到epoll 事件机制中等待监听套接字连接上的可读事件发生,

到此,Nginx 就可以接收并处理来自客户端的请求。当监听套接字连接上的可读事件发生时,即该连接上有来自客户端发出的连接请求,

则会启动读read事件的handler 回调方法ngx_event_accept,在ngx_event_accept 方法中调用accept() 函数接收来自客户端的连接请求,

成功建立连接之后,ngx_event_accept 函数调用监听套接字ngx_listen_t上的handler 回调方法ls->handler(c)(该回调方法就是ngx_http_init_connection 主要用于初始化ngx_connection_t客户端连接)。

ngx_http_init_connection 会将rev->handler的回调函数修改成: ngx_http_wait_request_handler,

该回调函数主要用于处理read事件的数据读取。后续当有read事件上来的时候,就会回调ngx_http_wait_request_handler函数,而非ngx_event_accept函数

3、ngx_event_accept中会调用ls->handler回调函数ngx_http_init_connection

4、ngx_http_init_connection初始化一个http的连接,并且将rev->handler回调函数修改成ngx_http_wait_request_handler,

主要用于接收read事件数据。所有后面进入事件循环后,read事件调用的是ngx_http_wait_request_handler函数。

//设置ngx_listening_t的handler,这个handler会在监听到客户端连接时被调用,具体就是在ngx_event_accept函数中,ngx_http_init_connection函数顾名思义,就是初始化这个新建的连接
void
ngx_http_init_connection(ngx_connection_t *c)
//当建立连接后开辟ngx_http_connection_t结构,这里面存储该服务器端ip:port所在server{}上下文配置信息,和server_name信息等,然后让
//ngx_connection_t->data指向该结构,这样就可以通过ngx_connection_t->data获取到服务器端的serv loc 等配置信息以及该server{}中的server_name信息 {
ngx_uint_t i;
ngx_event_t *rev;
struct sockaddr_in *sin;
ngx_http_port_t *port;
ngx_http_in_addr_t *addr;
ngx_http_log_ctx_t *ctx;
ngx_http_connection_t *hc;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
ngx_http_in6_addr_t *addr6;
#endif //注意ngx_connection_t和ngx_http_connection_t的区别,前者是建立连接accept前使用的结构,后者是连接成功后使用的结构
hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
if (hc == NULL) {
ngx_http_close_connection(c);
return;
} //在服务器端accept客户端连接成功(ngx_event_accept)后,会通过ngx_get_connection从连接池获取一个ngx_connection_t结构,也就是每个客户端连接对于一个ngx_connection_t结构,
//并且为其分配一个ngx_http_connection_t结构,ngx_connection_t->data = ngx_http_connection_t,见ngx_http_init_connection
c->data = hc; /* find the server configuration for the address:port */ port = c->listening->servers; if (port->naddrs > 1) { /*
* there are several addresses on this port and one of them
* is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
* is required to determine a server address
*/
//说明listen ip:port存在几条没有bind选项,并且存在通配符配置,如listen *:port,那么就需要通过ngx_connection_local_sockaddr来确定
//究竟客户端是和那个本地ip地址建立的连接
if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { //
ngx_http_close_connection(c);
return;
} switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) c->local_sockaddr; addr6 = port->addrs; /* the last address is "*" */ for (i = 0; i < port->naddrs - 1; i++) {
if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
break;
}
} hc->addr_conf = &addr6[i].conf; break;
#endif default: /* AF_INET */
sin = (struct sockaddr_in *) c->local_sockaddr; addr = port->addrs; /* the last address is "*" */
//根据上面的ngx_connection_local_sockaddr函数获取到客户端连接到本地,本地IP地址获取到后,遍历ngx_http_port_t找到对应
//的IP地址和端口,然后赋值给ngx_http_connection_t->addr_conf,这里面存储有server_name配置信息以及该ip:port对应的上下文信息
for (i = 0; i < port->naddrs - 1; i++) {
if (addr[i].addr == sin->sin_addr.s_addr) {
break;
}
} /*
这里也体现了在ngx_http_init_connection中获取http{}上下文ctx,如果客户端请求中带有host参数,则会继续在ngx_http_set_virtual_server
中重新获取对应的server{}和location{},如果客户端请求不带host头部行,则使用默认的server{},见 ngx_http_init_connection
*/
hc->addr_conf = &addr[i].conf; break;
} } else { switch (c->local_sockaddr->sa_family) { #if (NGX_HAVE_INET6)
case AF_INET6:
addr6 = port->addrs;
hc->addr_conf = &addr6[0].conf;
break;
#endif default: /* AF_INET */
addr = port->addrs;
hc->addr_conf = &addr[0].conf;
break;
}
} /* the default server configuration for the address:port */
//listen add:port对于的 server{}配置块的上下文ctx
hc->conf_ctx = hc->addr_conf->default_server->ctx; ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
if (ctx == NULL) {
ngx_http_close_connection(c);
return;
} ctx->connection = c;
ctx->request = NULL;
ctx->current_request = NULL; c->log->connection = c->number;
c->log->handler = ngx_http_log_error;
c->log->data = ctx;
c->log->action = "waiting for request"; c->log_error = NGX_ERROR_INFO; rev = c->read;
rev->handler = ngx_http_wait_request_handler;
c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_V2)
/* 这里放在SSL的前面是,如果没有配置SSL,则直接不用进行SSL协商而进行HTTP2处理ngx_http_v2_init */
if (hc->addr_conf->http2) {
rev->handler = ngx_http_v2_init;
}
#endif #if (NGX_HTTP_SSL)
{
ngx_http_ssl_srv_conf_t *sscf; sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); if (sscf->enable || hc->addr_conf->ssl) { c->log->action = "SSL handshaking"; if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"no \"ssl_certificate\" is defined "
"in server listening on SSL port");
ngx_http_close_connection(c);
return;
} hc->ssl = 1; rev->handler = ngx_http_ssl_handshake;
}
}
#endif if (hc->addr_conf->proxy_protocol) {
hc->proxy_protocol = 1;
c->log->action = "reading PROXY protocol";
} /*
如果新连接的读事件ngx_event_t结构体中的标志位ready为1,实际上表示这个连接对应的套接字缓存上已经有用户发来的数据,
这时就可调用上面说过的ngx_http_init_request方法处理请求。
*/
//这里只可能是当listen的时候添加了defered参数并且内核支持,在ngx_event_accept的时候才会置1,才可能执行下面的if里面的内容,否则不会只需if里面的内容
if (rev->ready) {
/* the deferred accept(), iocp */
if (ngx_use_accept_mutex) { //如果是配置了accept_mutex,则把该rev->handler延后处理,
//实际上执行的地方为ngx_process_events_and_timers中的ngx_event_process_posted
ngx_post_event(rev, &ngx_posted_events);
return;
} rev->handler(rev); //ngx_http_wait_request_handler
return;
} /*
在有些情况下,当TCP连接建立成功时同时也出现了可读事件(例如,在套接字listen配置时设置了deferred选项时,内核仅在套接字上确实收到请求时才会通知epoll
调度事件的回调方法。当然,在大部分情况下,ngx_http_init_request方法和
ngx_http_init_connection方法都是由两个事件(TCP连接建立成功事件和连接上的可读事件)触发调用的
*/ /*
调用ngx_add_timer方法把读事件添加到定时器中,设置的超时时间则是nginx.conf中client_header_timeout配置项指定的参数。
也就是说,如果经过client_header_timeout时间后这个连接上还没有用户数据到达,则会由定时器触发调用读事件的ngx_http_init_request处理方法。
*/
ngx_add_timer(rev, c->listening->post_accept_timeout, NGX_FUNC_LINE); //把接收事件添加到定时器中,当post_accept_timeout秒还没有客户端数据到来,就关闭连接
ngx_reusable_connection(c, 1); if (ngx_handle_read_event(rev, 0, NGX_FUNC_LINE) != NGX_OK) { //当下次有数据从客户端发送过来的时候,会在ngx_epoll_process_events把对应的ready置1。
ngx_http_close_connection(c);
return;
}
}

5、ls->handler的回调函数是如何赋值的 什么时候设置为 ngx_http_init_connection

看一下ngx_http.c中的ngx_http_block函数,该函数在模块命令初始化的时候会回调,并且调用 
ngx_http_optimize_servers方法,并且进一步调用ngx_http_init_listening初始化的监听器,然后最终调用ngx_http_add_listening方法

 解析HTTP配置的流程
HTTP框架解析配置项ngx_http_module和ngx_
http_core_module模块,所谓的HTTP框架主要由这两个模块组成),下面解释每个流程
的意义。
1)主循环是指Nginx进程的主循环,主循环只有调用配置文件解析器才能
解析nginx.conf文件(这里的“主循环”是指解析全部配置文件的循环代码,图8-6的第4
步,为了便于理解,可以认为是Nginx框架代码在循环解析配置项)。
2)当发现配置文件中含有http{)关键字时,HTTP框架开始启动,这一过程详见10.7
节描述的ngx_http_block方法。
3) HTTP框架会初始化所有HTTP模块的序列号,并创建3个数组用于存储所有HTTP
模块的create—main- conf、create—srv—conf、create—loc—conf方法返回的指针地址,并把这3
个教组的地址保存到ngx_http_conf_ ctx-t结构中。
4)调用每个HTTP模块(当然也包括例子中的mytest模块)的create main conf.
create—srv_conf. create一loc—conf(如果实现的话)方法。
5)把各HTTP模块上述3个方法返回的地址依次保存到ngx_http_conf ctx_t结构体的
3个数组中。
6)调用每个HTTP模块的preconfiguration方法(如果实现的话)。
7)注意,如果preconfiguration返回失败,那么Nginx进程将会停止。
8) HTTP框架开始循环解析nginx.conf文件中http{...}里面的所有配置项,
过程到第19步才会返回。
9)配置文件解析器在检测到1个配置项后,会遍历所有的HTTP模块,
ngx_command_t数组中的name项是否与配置项名相同。
注意,这个
检查它们的
10)如果找到有1个HTTP模块(如mytest模块)对这个配置项感兴趣(如test- myconfig
配置项),就调用ngx_command_t结构中的set方法来处理。
11) set方法返回是否处理成功。如果处理失败,那么Nginx进程会停止。
12)配置文件解析器继续检测配置项。如果发现server{...)配置项,就会调用ngx_http_
core__ module模块来处理。因为ngx_http_core__ module模块明确表示希望处理server{}块下
的配置项。注意,这次调用到第18步才会返回。
13) ngx_http_core_module棋块在解析server{...}之前,也会如第3步一样建立ngx_
http_conf_ctx_t结构,并建立数组保存所有HTTP模块返回的指针地址。然后,它会调用每
个HTTP模块的create—srv_ conf、create- loc—conf方法(如果实现的话)。
14)将上一步各HTTP模块返回的指针地址保存到ngx_http_conf_ ctx-t对应的数组中。
15)开始调用配置文件解析器来处理server{...}里面的配置项,注意,这个过程在第17
步返回。
16)继续重复第9步的过程,遍历nginx.conf中当前server{...)内的所有配置项。
17)配置文件解析器继续解析配置项,发现当前server块已经遍历到尾部,说明server
块内的配置项处理完毕,返回ngx_http_core__ module模块。
18) http core模块也处理完server配置项了,返回至配置文件解析器继续解析后面的配
置项。
19)配置文件解析器继续解析配置项,这时发现处理到了http{...)的尾部,返回给
HTTP框架继续处理。
20)在第3步和第13步,以及我们没有列幽来的某些步骤中(如发现其他server块
或者location块),都创建了ngx_http_conf_ ctx_t结构,这时将开始调用merge_srv_conf、
merge_loc_conf等方法合并这些不同块(http、server、location)中每个HTTP模块分配的数
据结构。
21) HTTP框架处理完毕http配置项(也就是ngx_command_t结构中的set回调方法处
理完毕),返回给配置文件解析器继续处理其他http{...}外的配置项。
22)配置文件解析器处理完所有配置项后会告诉Nginx主循环配置项解析完毕,这时
Nginx才会启动Web服务器。 注意 并没有列出解析location{...)块的流程,实际上,解析location与解析
server并没有本质上的区别。
//从ngx_http_module模块里面的http命令解析走到这里
/*
cf空间始终在一个地方,就是ngx_init_cycle中的conf,使用中只是简单的修改conf中的ctx指向已经cmd_type类型,然后在解析当前{}后,重新恢复解析当前{}前的配置
参考"http" "server" "location"ngx_http_block ngx_http_core_server ngx_http_core_location ngx_http_core_location
*/
static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
//这里的cf是从ngx_conf_handler里面的if (cmd->type & NGX_DIRECT_CONF)判断里面确定了该cf为
{//图形化参考:深入理解NGINX中的图9-2 图10-1 图4-2,结合图看,并可以配合http://tech.uc.cn/?p=300看
char *rv;
ngx_uint_t mi, m, s;
ngx_conf_t pcf;
ngx_http_module_t *module;
ngx_http_conf_ctx_t *ctx;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t **cscfp;
ngx_http_core_main_conf_t *cmcf; /* the main http context */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
} //conf为ngx_conf_handler中的conf = confp[ngx_modules[i]->ctx_index];也就是conf指向的是ngx_cycle_s->conf_ctx[],
//所以对conf赋值就是对ngx_cycle_s中的conf_ctx赋值
*(ngx_http_conf_ctx_t **) conf = ctx; //图形化参考:深入理解NGINX中的图9-2 图10-1 图4-2,结合图看,并可以配合http://tech.uc.cn/?p=300看 /* count the number of the http modules and set up their indices */ ngx_http_max_module = 0;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
} ngx_modules[m]->ctx_index = ngx_http_max_module++; //二级类型按照在ngx_modules中的顺序排序
} /* the http main_conf context, it is the same in the all http contexts */ ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_http_max_module);
if (ctx->main_conf == NULL) {
return NGX_CONF_ERROR;
} /*
* the http null srv_conf context, it is used to merge
* the server{}s' srv_conf's
*/ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
} /*
* the http null loc_conf context, it is used to merge
* the server{}s' loc_conf's
*/ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
} /*
* create the main_conf's, the null srv_conf's, and the null loc_conf's
* of the all http modules
*/
//执行所有ngx_modules[m]->type = NGX_HTTP_MODULE的http模块的crate函数来创建对应模块的conf参数,用于后面保存从配置文件中解析出的参数信息
//http{}下为所有的NGX_HTTP_MODULES模块开辟了main srv loc空间
//按照模块类型进行合并 http{} server{} location{}都属于同一个ngx_http_core_module模块,他们的init_main_conf都是一样的
/*
http {
xxxx
server {
location /xxx {
}
}
}
这种情况的配置文件,在执行到http的时候开辟ngx_http_conf_ctx_t会分别调用一次main crv loc_creat,执行到server时开辟ngx_http_conf_ctx_t会调用srv_creat loc_creat, 执行到location时开辟ngx_http_conf_ctx_t会调用一次loc_creat
所以这种情况会调用1次main_creat 2才srv_creat 3次loc_creat。 http {
xxxx
server {
location /xxx {
}
} server {
location /yyy {
}
}
}
这种情况的配置文件,在执行到http的时候开辟ngx_http_conf_ctx_t会分别调用一次main crv loc_creat,执行到server时开辟ngx_http_conf_ctx_t会调用srv_creat loc_creat, 执行到location时开辟ngx_http_conf_ctx_t会调用一次loc_creat
所以这种情况会调用1次main_creat 1+2才srv_creat 1+2+2次loc_creat。 需要ngx_http_block ngx_http_core_server ngx_http_core_location配合看代码可以看出来
*/
for (m = 0; ngx_modules[m]; m++) { //注意这里为所有的NGX_HTTP_MODULE开辟了main_conf srv_conf loc_conf空间,也就是在http{}的时候为所有main srv loc开辟了空间
if (ngx_modules[m]->type != NGX_HTTP_MODULE) { //http{}相关配置结构创建首先需要执行ngx_http_core_module,而后才能执行对应的http子模块
continue;
} module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index; //mi实际上是依次递增的,见签名的ctx_index赋值处 if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
if (ctx->main_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
} if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
if (ctx->srv_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
} if (module->create_loc_conf) {
ctx->loc_conf[mi] = module->create_loc_conf(cf);
if (ctx->loc_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
} pcf = *cf; //零时保存在解析到http{}时候,在这之前的cf
cf->ctx = ctx;//零时指向这块新分配的ctx,为存储ngx_http_core_commands开辟的空间 //执行各个模块的preconfiguration
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
} module = ngx_modules[m]->ctx; if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
} /* parse inside the http{} block */ cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
rv = ngx_conf_parse(cf, NULL);
if (rv != NGX_CONF_OK) {
goto failed;
} /*
* init http{} main_conf's, merge the server{}s' srv_conf's
* and its location{}s' loc_conf's
*/ cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; //见ngx_http_core_create_main_conf
cscfp = cmcf->servers.elts;//一级main_conf中的server中保存的所有二级server结构信息 for (m = 0; ngx_modules[m]; m++) { //按照模块类型进行合并 http{} server{} location{}都属于同一个ngx_http_core_module模块,他们的init_main_conf都是一样的
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
} module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index; /* init http{} main_conf's */ if (module->init_main_conf) {
rv = module->init_main_conf(cf, ctx->main_conf[mi]); //见ngx_http_core_init_main_conf
if (rv != NGX_CONF_OK) {
goto failed;
}
} //cf->ctx为http{}的上下文ctx,cmcf为server{}中的所有上下文ctx
rv = ngx_http_merge_servers(cf, cmcf, module, mi);//合并server{}及其以下的local{}
if (rv != NGX_CONF_OK) {
goto failed;
}
} /* create location trees */
/*
经过配置的读取之后,所有server都被保存在http core模块的main配置中的servers数组中,而每个server里面的location都被按配置中
出现的顺序保存在http core模块的loc配置的locations队列中,上面的代码中先对每个server的location进行排序和分类处理,这一步
发生在 ngx_http_init_location()函数中:
*/
for (s = 0; s < cmcf->servers.nelts; s++) {
/*
clcf是server块下的ngx_http_core_loc_conf_t结构体,locations成员以双向链表关联着隶属于这个server块的所有location块对应的ngx_http_core_loc_conf_t结构体
*/
//cscfp[]->ctx就是解析到二级server{}时所在的上下文ctx
clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];//每个server中的loc空间,其实他也是该server下location{}中的loc空间的头部,参考ngx_http_add_location /*
将ngx_http_core_loc_conf_t组成的双向链表按照location匹配字符串进行排序。注意:这个操作是递归进行的,如果某个location块下还具有其他location,那么它的locations链表也会被排序
*/
if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
//srver{}下所有loc空间(包括server自己的以及其下的location),这里的clcf是解析到server{}行的时候创建的loc_conf
return NGX_CONF_ERROR;
} /*
根据已经按照location字符串排序过的双向链表,快速地构建静态的二叉查找树。与ngx_http_init_locations方法类似,速个操作也是递归进行的
*/
/*
下面的ngx_http_init_static_location_trees函数就会将那些普通的location(就是ngx_http_init_locations中name noname regex以外的location(exact/inclusive)),
即staticlocation,进行树化(一种三叉树)处理,之所以要做这样的处理,是为了在处理http请求时能高效的搜索的匹配的location配置。
*/
/*
根据已经按照location字符串排序过的双向链表,快速地构建静态的三叉查找树。与ngx_http_init_locations方法类似,速个操作也是递归进行的
*/ //clcf中现在只有普通staticlocation
if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
} if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
} if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
} for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
} module = ngx_modules[m]->ctx; if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
} if (ngx_http_variables_init_vars(cf) != NGX_OK) {
return NGX_CONF_ERROR;
} /*
* http{}'s cf->ctx was needed while the configuration merging
* and in postconfiguration process
*/ *cf = pcf;//恢复到上层的ngx_conf_s地址 if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
} /* optimize the lists of ports, addresses and server names */
if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
return NGX_CONF_ERROR;
} return NGX_CONF_OK; failed: *cf = pcf; return rv;
} /*
这个函数就是遍历所有的端口号,将端口号对应的地址结构的hash、wc_head和wc_tail初始化,这个在初始化后面的ngx_listening_t的servers字
段时会用到。然后调用ngx_http_init_listening函数完成ngx_listening_t初始化。
*/
static ngx_int_t
ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
ngx_array_t *ports)
{
ngx_uint_t p, a;
ngx_http_conf_port_t *port;
ngx_http_conf_addr_t *addr; if (ports == NULL) {
return NGX_OK;
} port = ports->elts;
for (p = 0; p < ports->nelts; p++) {
//将addrs排序,带通配符的地址排在后面, (listen 1.2.2.2:30 bind) > listen 1.1.1.1:30 > listen *:30
ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs); /*
* check whether all name-based servers have the same
* configuration as a default server for given address:port
*/ addr = port[p].addrs.elts;
for (a = 0; a < port[p].addrs.nelts; a++) {
/* 多个server{}下面有listen IP:port ,并且每个server{}中的端口都相等,则他们保存在同一个port[i]中,只是ip地址不一样,以addrs区分 */
if (addr[a].servers.nelts > 1
#if (NGX_PCRE)
|| addr[a].default_server->captures
#endif
)
{ //相同端口,不同IP地址对应的server{},把每个server中的server_names配置进行hash存储
/*
初始addr(ngx_http_conf_addr_t)中的hash、wc_head和wc_tail哈希表。 这些哈希表以server_name(虚拟主机名)为key,server块
的ngx_http_core_srv_conf_t为 value,用于在处理请求时,根据请求的host请求行快速找到处理该请求的server配置结构。
*/
if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
return NGX_ERROR;
}
}
} if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
return NGX_ERROR;
}
} return NGX_OK;
} static ngx_int_t
ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
{
ngx_uint_t i, last, bind_wildcard;
ngx_listening_t *ls;
ngx_http_port_t *hport;
ngx_http_conf_addr_t *addr; addr = port->addrs.elts;
last = port->addrs.nelts; /*
* If there is a binding to an "*:port" then we need to bind() to
* the "*:port" only and ignore other implicit bindings. The bindings
* have been already sorted: explicit bindings are on the start, then
* implicit bindings go, and wildcard binding is in the end. //例如有listen 80(implicit bindings); listen *:80,则第一个无效,直接用第二个就行了
*/ if (addr[last - 1].opt.wildcard) { //"*:port" addr是拍了序的,见ngx_http_optimize_servers,最后面的是通配符
addr[last - 1].opt.bind = 1; //如果是通配符,这里把bind值1
bind_wildcard = 1; //表示有通配符listen } else {
bind_wildcard = 0;
} i = 0; /*
这个函数就是遍历某个端口port对应的所有address,如果所有address中不包含通配符,则对所有的address:port调用ngx_http_add_listening分配一
个listen结构和ngx_http_port_t结构,并初始化它们。如果存在address包含通配符,则如果address:port需要bind,分配一个listen结构和
ngx_http_port_t结构,并初始化它们,对所有address:port不需要bind的,它们和包含通配符*:port共同使用一个listen结构和ngx_http_port_t结构,
并且listen结构中包含的地址是*:port,所以最好bind的地址是*:port。所有的listen都会存放在全局变量ngx_cycle的listening数组中,这样后面就
可以利用这些address:port信息建立每个套接字了。
*/
while (i < last) {
//last代表的是address:port的个数, 如果没有通配符配置项,则有多少个last,就有多少次循环。bind=1的有多少次就执行多少次,如果有通配符和bind = 0的listen配置,
//则在后面的if (bind_wildcard && !addr[i].opt.bind)进行continue,也就是这些未精确配置项合在一起在后面置执行一次分配ngx_http_port_t空间,把他们算在
//addr[i]中,这里的i是通配符所在位置。 //对所有address:port不需要bind的,它们和包含通配符*:port共同使用一个listen结构和ngx_http_port_t结构, 并且listen结构中包含的地址是*:port,所以最好bind的地址是*:port
if (bind_wildcard && !addr[i].opt.bind) { //如果是通配符*:port,或者是listen配置没有加bind参数
i++;//如果有通配符配置,并且bind = 0则把这些bind=0和通配符配置算作一项,执行后面的操作。通配符的bind在该函数前面置1,见addr[last - 1].opt.bind = 1
continue;
} //为该listen创建对应的ngx_listening_t结构并赋值
ls = ngx_http_add_listening(cf, &addr[i]);
if (ls == NULL) {
return NGX_ERROR;
} hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
if (hport == NULL) {
return NGX_ERROR;
} /*
* servers会用来保存虚拟主机的信息,在处理请求时会赋值给request 用于进行虚拟主机的匹配
*/
ls->servers = hport; //如果是未精确配置的listen(bind = 0并且有配置一项通配符,则这里的i是通配符所在addr[]的位置),如果没有配置通配符,则有多少个listen配置就会执行这里多少次。
//只是在出现通配符listen的配置中,把未精确配置的所有项合到通配符所在addr[]位置
hport->naddrs = i + 1; //保护listen通配符配置,并且没有bind的listen项数
switch (ls->sockaddr->sa_family) { #if (NGX_HAVE_INET6)
case AF_INET6:
if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) {
return NGX_ERROR;
}
break;
#endif
default: /* AF_INET */
if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) { //后面有addr++,所以这里的addr对应的是addr[i]的地址
return NGX_ERROR;
}
break;
} if (ngx_clone_listening(cf, ls) != NGX_OK) {
return NGX_ERROR;
} addr++;
last--;
} return NGX_OK;
} //ngx_event_process_init
//master进程执行ngx_clone_listening中如果配置了多worker,监听80端口会有worker个listen赋值,master进程在ngx_open_listening_sockets
//中会监听80端口worker次,那么子进程创建起来后,不是每个字进程都关注这worker多个 listen事件了吗?为了避免这个问题,nginx通过
//在子进程运行ngx_event_process_init函数的时候,通过ngx_add_event来控制子进程关注的listen,最终实现只关注master进程中创建的一个listen事件 //ngx_listening_t创建空间,并通过addr赋值初始化
static ngx_listening_t *
ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
{
ngx_listening_t *ls;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf; //为listen配置创建对应的ngx_listening_t结构,并赋值IP地址等,里面也会完成IP地址字符串格式的转换
ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen);
if (ls == NULL) {
return NULL;
} ls->addr_ntop = 1; // 设置ngx_listening_t的handler,这个handler会在监听到客户端连接时被调用,具体就是在ngx_event_accept函数中,ngx_http_init_connection函数顾名思义,就是初始化这个新建的连接
ls->handler = ngx_http_init_connection; cscf = addr->default_server;
ls->pool_size = cscf->connection_pool_size;
ls->post_accept_timeout = cscf->client_header_timeout; clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; ls->logp = clcf->error_log;
ls->log.data = &ls->addr_text;
ls->log.handler = ngx_accept_log_error; //该listen上面的打印会加上listen后面的IP地址字符串格式 #if (NGX_WIN32)
{
ngx_iocp_conf_t *iocpcf = NULL; if (ngx_get_conf(cf->cycle->conf_ctx, ngx_events_module)) {
iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
}
if (iocpcf && iocpcf->acceptex_read) {
ls->post_accept_buffer_size = cscf->client_header_buffer_size;
}
}
#endif ls->backlog = addr->opt.backlog;
ls->rcvbuf = addr->opt.rcvbuf;
ls->sndbuf = addr->opt.sndbuf; ls->keepalive = addr->opt.so_keepalive;
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
ls->keepidle = addr->opt.tcp_keepidle;
ls->keepintvl = addr->opt.tcp_keepintvl;
ls->keepcnt = addr->opt.tcp_keepcnt;
#endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
ls->accept_filter = addr->opt.accept_filter;
#endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
ls->deferred_accept = addr->opt.deferred_accept;
#endif #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
ls->ipv6only = addr->opt.ipv6only;
#endif #if (NGX_HAVE_SETFIB)
ls->setfib = addr->opt.setfib;
#endif #if (NGX_HAVE_TCP_FASTOPEN)
ls->fastopen = addr->opt.fastopen;
#endif #if (NGX_HAVE_REUSEPORT)
ls->reuseport = addr->opt.reuseport;
#endif return ls;
}

nginx&http 第二章 ngx 事件event初始化 ngx_event_process_init的更多相关文章

  1. nginx&http 第二章 ngx 事件event处理 数据结构

    ngx_event.c :这个文件主要放置Nginx事件event模块的核心代码. 包含:进程事件分发器(ngx_process_events_and_timers).事件模块的模块和配置.模块初始化 ...

  2. nginx&http 第二章 ngx 事件event配置等初始化

    event事件模块,配置分为两层:ngx_events_module 事件模块 和 ngx_event_core_module 事件核心模块.ngx_events_module:模块类型NGX_COR ...

  3. nginx&http 第三章 ngx 事件http 初始化1

    在 http 配置块中,我们配置了 http 连接相关的信息,HTTP 框架也正是从这里启动的 在 nginx 初始化的过程中,执行了 ngx_init_cycle 函数,其中进行了配置文件解析,调用 ...

  4. nginx&http 第三章 ngx 事件event epoll 处理

    1. epoll模块命令集 ngx_epoll_commands  epoll模块上下文 ngx_epoll_module_ctx  epoll模块配置 ngx_epoll_module static ...

  5. nginx&http 第三章 ngx 事件event accept epoll /init

    tcp 三次握手成功后,listen fd  可读,在process_event_timer 中调用rev->handler(rev)处理: 其回调函数为: ngx_event_accept / ...

  6. nginx&http 第二章 ngx启动多进程

    Nginx服务器使用 master/worker 多进程模式. 主进程(Master process)启动后,会接收和处理外部信号: 主进程启动后通过fork() 函数产生一个或多个子进程(work ...

  7. nginx&http 第三章 ngx http ngx_http_process_request_line读取和处理HTTP头部的行

    在 ngx_http_wait_request_handler 的最后调用了 ngx_http_process_request_line 函数用来处理和解析这次请求的全文 在读事件被触发时,内核套接字 ...

  8. nginx&http 第三章 ngx http 框架处理流程

    1. nginx 连接结构 ngx_connection_t 这个连接表示是客户端主动发起的.Nginx服务器被动接受的TCP连接,我们可以简单称其为被动连接.同时,在有些请求的处理过程中,Nginx ...

  9. Prism 文档 第二章 初始化Prism应用程序

                                                                           第二章 初始化Prism应用程序 本章将讨论为了使一个Pr ...

随机推荐

  1. 使用Spring Boot创建docker image

    目录 简介 传统做法和它的缺点 使用Buildpacks Layered Jars 自定义Layer 简介 在很久很久以前,我们是怎么创建Spring Boot的docker image呢?最最通用的 ...

  2. go init执行顺序

    package test import "fmt" // 初始化函数 引入包的时候要先执行 可以重复定义多个 同一个go文件从上到下 多个文件 是按照字符串进行排序 从小到大 执行 ...

  3. SOAP调用Web Service

    SOAP调用Web Service (示例位置:光盘\code\ch07\ WebAppClient\ JsService4.htm) <html xmlns="http://www. ...

  4. 第二十四章 Find命令详细介绍

    一.Find 概述 可以根据文件的名称.文件大小.文件的修改时间.文件的类型.文件的权限.文件的属主属组.文件的目录层级进行查找 Find的语法: find [-H] [-L] [-P] [-Olev ...

  5. Anderson《空气动力学基础》5th读书笔记 第2记——流体静力学初步

    与物体在水中受到水的浮力一样,空气中的物体也会受到空气的浮力,但由于这个浮力往往比较小,实际中的很多问题我们常常将它忽略,而对于像热气球这样的靠空气的浮力产生升力的飞行器来说,空气的浮力是不能忽略的. ...

  6. Linux下使用select延时

    在LINUX用户态的情况下,如果想要延时的话,可以使用用sleep函数,但是在一些情况下,需要更小单位的延时,ms/us 也是要的.用循环获取到的延时是不精确的. sleep是不准确,这个函数是可以中 ...

  7. eclipse安装报错

    例如这样 原因是被墙了 个人搭**后完美解决

  8. mysql一些使用函数(不断更新)

    1.中文转拼音码,多用于将姓名转成拼音(例如:刘德华:liudehua) /*建立拼音码表*/ CREATE TABLE IF NOT EXISTS `t_base_pinyin` ( `pin_yi ...

  9. ansible-hoc命令行

    ansible一种开源的自动化工具 ansible: hoc命令行: 是一款开源的自动化运维工具 python paramiko #模拟ssh协议批量管理主机 jinja2 #模板语言,主要用来传递变 ...

  10. 将java的jar包,打包为rpm 安装包

    一.rpm包 介绍 RPM Package Manager (RPM) 是一个强大的命令行驱动的软件包管理工具,用来安装.卸载.校验.查询和更新 Linux 系统上的软件包 二.环境安装 一台cent ...