[nginx源码分析]nginx事件逻辑

开始分析函数main->ngx_master_process_cycle

ngx_master_process_cycle

首先设置信号屏蔽sigprocmask,然后设置master进程title

然后创建子进程调用

ngx_start_worker_processes(cycle,ccf->worker_processes, NGX_PROCESS_RESPAWN)

/*
  * cycle是在main创建的
  * n 表示需要创建多少个进程worker_process
  * type 为-3    NGX_PROCESS_RESPAWN
  */
static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
     ngx_int_t      i;
     ngx_channel_t  ch;

     ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");

     ch.command = NGX_CMD_OPEN_CHANNEL;

     //for循环就是为了创建三个进程
     for (i = 0; i < n; i++) {

         cpu_affinity = ngx_get_cpu_affinity(i);

         //主要是设置ngx_process和子进程在该数组中的索引
         //其中fork完子进程后,会调用ngx_worker_process_cycle(cycle, NULL)
         ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,                                                                                                  
                           "worker process", type);

         ch.pid = ngx_processes[ngx_process_slot].pid;
         ch.slot = ngx_process_slot;
         ch.fd = ngx_processes[ngx_process_slot].channel[0];
         //主要是把ch信息写到前面的子进程里面去,每个子进程都可以进行通信,同时任意父子进程也可以进行通信
         ngx_pass_open_channel(cycle, &ch);
     }
}

函数主要是根据需要创建woker_process个数来创建多少个进程(for循环),子进程函数为ngx_worker_process_cycle,然后把子进程和父进程通信的socketpair通信句柄传递给前面创建的子进程,channel主要是父进程和子进程进行通信,同时该功能还可以子进程和子进程进行通信,貌似子进程和子进程没有通过channel通信。ngx_pass_open_channel主要把刚刚创建子进程的channel信息写到前面已经创建的子进程中去。

 

然后master进程fork子进程调用ngx_worker_process_cycle

ngx_worker_process_cycle(cycle, NULL)

此函数就是子进程的主要服务代码,包含了事件初始化、事件触发、事件处理。分析两个主要函数:

ngx_worker_process_init(cycle, 1)//子进程初始化

ngx_process_events_and_timer(cycle)//事件触发和事件处理

其中区别master和worker进程标识字段是变量ngx_process

master			NGX_PROCESS_MASTER
worker			NGX_PROCESS_WORKER

那么先来分析事件初始化函数

ngx_worker_process_init(cycle, 1)

函数做了如下几件事情

1 设置进程权限(setpriority(PRIO_PROCESS,0, ccf->priority)

2 设置软硬fd连接数(setrlimit(RLIMIT_NOFILE,&rlmt))

3 设置进程core文件大小(setrlimit(RLIMIT_CORE,&rlimit))

4 设置用户组…

5 更改工作目录(chdir)

6 调用进程模块中的init_process函数,来初始化每一个模块的进程初始化回调

for (i = 0; ngx_modules[i]; i++) {
//在子进程创建后,调用每一个模块的初始化进程函数init_process
if (ngx_modules[i]->init_process) {
if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
    exit(2);
    }
}
}

其中这里涉及到ngx_event_core_module事件模块的的初始化,先说下模块函数回调过程

模块定义三个回调函数分别如下:

struct ngx_module_s{
	…..
     ngx_int_t           (*init_master)(ngx_log_t *log);                                                                                                                                                                                 
 
     ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

     ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
…….
}

其中init_master现在还没有使用。

init_module是在ngx_init_cycle函数中调用的

ngx_cycle_t* ngx_init_cycle(ngx_cycle_t *old_cycle)
{
	……….
     for (i = 0; ngx_modules[i]; i++) { 
         if (ngx_modules[i]->init_module) {
             if (ngx_modules[i]->init_module(cycle) != NGX_OK) {                                                                                                                                                                        
                 /* fatal */
                 exit(1);
             }
         }
     }
	…………
}

init_process是在ngx_worker_process_init中调用的

static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority)
{
		……..
     for (i = 0; ngx_modules[i]; i++) {
         //在子进程创建后,调用每一个模块的初始化进程函数init_process
         if (ngx_modules[i]->init_process) {
             if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
                 /* fatal */
                 exit(2);
             }
         }
     }
	………
}

事件的初始化调用ngx_event_core_module 模块的init_module和init_process函数,首先分析init_module因为init_module是在init_process函数前面调用。其中

static ngx_int_t ngx_event_module_init(ngx_cycle_t*cycle主要是做一些valid验证

然后分析init_process回调函数,相应的函数为

static ngx_int_t ngx_event_process_init(ngx_cycle_t* cycle)
{
	…………………
//master是用户设置master_process on 或者配置没有设置。默认会被设置为1
//worker_processes 是用户配置的用户进程个数
//accept_mutex 是用户配置的是否开启使用accept_mutex on/off 默认是1 开启状态                                                                                                                                                        
if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
ngx_use_accept_mutex = 1;//是否开启负载均衡
ngx_accept_mutex_held = 0;//设置进程没有获取mutex
ngx_accept_mutex_delay = ecf->accept_mutex_delay;//epoll_wait的超时时间,用户配置的值
} else {
ngx_use_accept_mutex = 0;
}
……
ngx_event_timer_init(cycle->log)//初始化超时红黑树
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
//判断使用哪一个网络事件模块是在ngx_event_core_init_conf设置use
if (ngx_modules[m]->ctx_index != ecf->use) {
continue;
}
module = ngx_modules[m]->ctx;
//调用epoll模块的初始化函数, 主要是把ngx_event_actions 指向 epoll的操作函数
//调用模块的初始化函数
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
/* fatal */
exit(2);
}
break;
}
……
//分配连接
cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
//分配读事件
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
//分配写事件
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
do {
i--;

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];
#if (NGX_THREADS)
c[i].lock = 0;
#endif                                                                                                                                                                                                                                 
} while (i);
cycle->free_connections = next;//free链表指向connection链表
cycle->free_connection_n = cycle->connection_n;//设置connection个数
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
//申请一块连接内存ngx_connection_t
c = ngx_get_connection(ls[i].fd, cycle->log);

if (c == NULL) {
return NGX_ERROR;
}

c->log = &ls[i].log;
c->listening = &ls[i];
ls[i].connection = c;//设置listening的connection为监听的connection,用于对事件进行监听
rev = c->read;
rev->log = c->log;
//设置accept为1表示此connect是listen套接字
rev->accept = 1;
//设置连接rev回调为ngx_event_accept,当一个连接进行连接的时候就会调用这个函数
rev->handler = ngx_event_accept;
//因为如果用多进程共享锁,那么后面worker进程会通过加锁方式来对这些监听事件互斥访问
//如果进程负载均衡那么就不加入监控事件中去
if (ngx_use_accept_mutex) {
continue;
}
if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
if (ngx_add_conn(c) == NGX_ERROR) {
return NGX_ERROR;
}
} else {
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
}
}

static ngx_int_t ngx_event_process_init(ngx_cycle_t*cycle)函数主要完成以下功能

1 开启对监听套接字的负载均衡

2 初始化超时红黑树

3 对使用的io复用模型初始化(epoll模型)

4 分配连接(cycle->connections)、分配读事件(cycle->read_events)、分配写事件(cyvle->write_events)

5 一个连接配置一个读事件和一个写事件

6 对监听分配connection,并且保存在cycle->listening.connection中,同时设置rev事件的handler为ngx_event_accept

 

然后再回到ngx_worker_process_init函数,最后调用ngx_add_channel_event,此函数主要是把和父进程通信的sockpair加入到epoll网络事件循环中去,方便父子进程之间进行通信

 

然后继续分析第二个函数

ngx_process_events_and_timers(cycle)
{
	……………
//超时有两种算法
if (ngx_timer_resolution) {
//第一种是定时,epoll_wait 时间设置成infinite用定时器来激活epoll_wait
timer = NGX_TIMER_INFINITE;                                                                                                                                                                                                    
flags = 0;                
} else {
//默认是第二种
//第二种用最小超时时间,获取最小超时时间方法就是从红黑超时树中获取
timer = ngx_event_find_timer();
flags = NGX_UPDATE_TIME;
	}
if (ngx_use_accept_mutex) {                                                                                                                                                                                                        
if (ngx_accept_disabled > 0) {
ngx_accept_disabled--;
} else {
//如果能拿到那么就已经加入到epoll中去了
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
}
//如果加锁成功,那么事件会移后处理
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;
} else {
if (timer == NGX_TIMER_INFINITE || timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
 	}
/*
	* cycle 
	* timer epoll_wait设置的超时时间
	* flags 设置的标志,用于表示收到网络事件后的操作,比如移后处理。
	* 此函数映射到epoll复用模块的ngx_epoll_process_events函数,后面会分析
	*/
	(void) ngx_process_events(cycle, timer, flags)
	/*函数完成的操作有
	*1 如果该进程抢占到监听套接字并且接受连接读事件是一个accept sock,并且flag设置NGX_POST_EVENT延后处理,那么就把连接rev加入ngx_posts_accept_events,如果是普通连接加入到ngx_post_events,否则就直接调用rev->handler方法 
	*2 如果该进程抢占到监听套接字,设置flag为延后处理,那么就把该wev事件加入到ngx_post_events链表里面,如果没有进行延后处理,那么就直接调用写的回调函数
wev->handler(wev)
	*
	*/
		//如果post accept事件链表不为空,那么就进行处理
if (ngx_posted_accept_events) {                                                                                                                                                                               
         ngx_event_process_posted(cycle, &ngx_posted_accept_events);                                                                                                                                               
     }                                                                                                                                                                                                             
     //解锁,accept用完了                                                                                                                                                                                                              
     if (ngx_accept_mutex_held) {                                                                                                                                                                                  
         ngx_shmtx_unlock(&ngx_accept_mutex);                                                                                                                                                                      
     }                                                                                                                                                                                                             
	     //处理超时                                                                                                                                                                                           
     if (delta) {                                                                                                                                                                                                  
         ngx_event_expire_timers();                                                                                                                                                                                
     }                                                                                                                                                                                                             
                                                                                                                                                                                                                   
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,                                                                                                                                                            
 	                    "posted events %p", ngx_posted_events);                                                                                                                                                        
		 //处理已经连接事件
     if (ngx_posted_events) {                                                                                                                                                                                      
         if (ngx_threaded) {                                                                                                                                                                                       
              ngx_wakeup_worker_thread(cycle);                                                                                                                                                                                           
  
         } else {
              ngx_event_process_posted(cycle, &ngx_posted_events);
 	         }
 	     }
 }

一开始每一个进程都是都是加入到监听套接口到epoll中去,然后开始的事件肯定是连接事件,那么也就是调用rev->handler,此时的handler真实回调函数为ngx_event_accept,那么我们就开始分析ngx_event_accept。

ngx_event_accept函数主要完成如下几点功能

1 accept获取client连接

2 设置ngx_accept_disabled= ngx_cycle->connection_n/8 – ngx_cycle->free_connection_n

3 ngx_get_connection(s, ev->log)给client连接分配connection

4 连接初始化

5 调用accept sock的ls->handler此函数为ngx_http_init_connection

 

然后接着分析ngx_http_init_connection

函数主要是设置rev和wev事件的回调函数,如果读事件已经准备好了,那么处理,如果没有准备好,那么就加入epoll事件中,如果在epoll事件中有数据已经读取完毕,还是会调用rev->handler,也就是ngx_http_init_request函数。

可以看到函数处理流程相应的client connection的rev和wev handler的变化情况表

Func

rev->handler

wev->handler

ngx_http_init_connection

ngx_http_init_request

ngx_http_empty_handler

ngx_http_init_request

ngx_http_process_request_line

ngx_http_empty_handler

ngx_http_process_request_line

ngx_http_process_request_headers

ngx_http_empty_handler

 处理请求line和header之后,调用ngx_http_process_request

static void ngx_http_process_request(ngx_http_request_t*r)

然后调用ngx_http_handler()

然后调用ngx_http_core_run_phase()

此函数会回调每一个模块注册的函数

void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
     ngx_int_t                   rc;
     ngx_http_phase_handler_t   *ph;
     ngx_http_core_main_conf_t  *cmcf;

     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

     ph = cmcf->phase_engine.handlers;

     while (ph[r->phase_handler].checker) {

         rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

         if (rc == NGX_OK) {
             return;
         }
     }
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值