转自:http://bollaxu.iteye.com/blog/855457
事件处理是Nginx处理请求的核心,每个子进程在ngx_wrker_process_cycle()的循环里面不断调用
ngx_procss_events_and_timers()函数来处理各种事件。下面,分析使用epoll机制下的Nginx事件处理过程,
用源码分析和debug信息追踪两种方法。我们从ngx_worker_process_cycle()函数(即工作进程处理请求的循环)切入:
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
/*...*/
//第一部分:初始化
ngx_worker_process_init(cycle, 1);
/*...*/
for ( ;; ) {
/*...*/
//第二部分:处理事件
ngx_process_events_and_timers(cycle);
/*...*/
}
/*...*/
}
//第一部分:初始化
static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority)
{
//配置一些环境变量
//...
//设置uid,groupid等
//...
//如果有设置CPU affinity
//...
//换到当前工作的目录下
//...
//清空所有的信号
//...
//清掉监听socket上以前的事件
//...
//调用所有模块的init_process钩子函数
//所有模块-->每个子进程可以调用这些模块的功能
//init_process
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->init_process) {
//如果是event module: ngx_event_process_init()被调用
if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
exit(2);
}
}
}
//将其他进程的channel[1]关闭,自己的除外
//子进程继承了父进程的ngx_processes数组,但子进程只监听自己的channel[1]
//...
//将自己的channel[0]关闭
//因为自己的channel[0]是给其他子进程,用来发送消息的sendmsg
//...
//调用ngx_add_channel_event()函数,给ngx_channel注册一个读事件处理函数。
//在ngx_start_worker_processes()函数中,ngx_channel = ngx_processes[s].channel[1];
//ngx_channel就是进程自身的channel[1],用来读取的socket
//ngx_channel_handler处理从channel中收到的信号,当事件触发时,调用这个方法
if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, ngx_channel_handler) == NGX_ERROR)
{
exit(2);
}
}
每个模块(module)都有一个全局的ngx_module_t结构变量,在worker process被创建(fork)以后,在ngx_worker_process_init()内调用到每个模块的init_process钩子。其中ngx_event_core_module的init_process钩子指向的是ngx_event_process_init()函数,这个函数是这样的:
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle)
{
/*...*/
for (m = 0; ngx_modules[m]; m++) {
//只有ngx_event_core_module和ngx_epoll_module是NGX_EVENT_MODULE类型的
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
if (ngx_modules[m]->ctx_index != ecf->use) {
continue;
}
//获取模块上下文
module = ngx_modules[m]->ctx;
//初始化模块
//ngx_epoll_module(类型ngx_module_t)是全局的结构变量,在初始化的时候由ngx_epoll_module_ctx传入参数,而init函数也在这个时候确定
//如epoll就是ngx_epoll_init
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
exit(2);
}
break;
}
/*...*/
/*=========初始化connections=========*/
//分配connection_n个空间给connections
cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
c = cycle->connections;
//分配connection_n个空间给read_events和write_events,初始化这些read_events和write_events
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
rev = cycle->read_events;
for (i = 0; i < cycle->connection_n; i++) {
rev[i].closed = 1;
rev[i].instance = 1;
/*不考虑线程*/
}
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
wev = cycle->write_events;
for (i = 0; i < cycle->connection_n; i++) {
wev[i].closed = 1;
/*不考虑线程*/
}
//把connection和read_event,write_event联系起来,每个connection都指向一个read_event和write_event
i = cycle->connection_n;
next = NULL;
do {
//把connection链接起来
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];
/*不考虑线程*/
} while (i);
//free_connections指向connections的头
cycle->free_connections = next;
//初始化free connection的数目
cycle->free_connection_n = cycle->connection_n;
/* for each listening socket */
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
c = ngx_get_connection(ls[i].fd, cycle->log);//获取一个空闲的connection,并设置其file descriptor
c->log = &ls[i].log;
c->listening = &ls[i];//获取ngx_listening_s结构
ls[i].connection = c; //设置ngx_listening_s.connection
rev = c->read;
rev->log = c->log;
rev->accept = 1; //接受请求
/*...*/
//设置c->read的handler为ngx_event_accept
rev->handler = ngx_event_accept;
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;
}
}
}
}
在上面的函数里,如果我们使用了epoll,那么epoll模块的ngx_epoll_init()函数就会被调用,而这个函数中最重要的就是
ngx_event_actions = ngx_epoll_module_ctx.actions
在工作进程的for循环中用来处理事件的ngx_process_events_and_timers()中,每次调用ngx_process_events()的时候,其实就是调用ngx_epoll_module_ctx.actions里面的ngx_epoll_process_events()。
//第二部分:处理事件
//被循环调用
//先接收连接(并不处理事件),以及处理进程间信号(如有)
//处理accept queue和event queue里面的事件
void ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
//如果使用了accept mutex
//nginx uses accept mutex to serialize accept() syscalls
//多进程需要使用mutex
if (ngx_use_accept_mutex) {
//空闲连接数过少
if (ngx_accept_disabled > 0) {
ngx_accept_disabled--;
}
else {
//调用ngx_trylock_accept_mutex()试着给cycle上锁,并把ngx_accept_mutex_held设为1
//如果成功获得lock,将调用ngx_enable_accept_events()
//ngx_enable_accept_events()中会调用ngx_add_event()/ngx_add_conn()
//即,获得lock的worker process才会添加一个“接受请求”的事件
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
}
//ngx_accept_mutex_held表示当前是否已经持有锁
//如果持有的话,就把flags添加NGX_POST_EVENTS,这样表明可以去accept请求
//如果不持有,就去处理其他事件,在ngx_epoll_process_events里会调用
//rev/wev->handler()
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;
}
else {
if (timer == NGX_TIMER_INFINITE ||
timer > ngx_accept_mutex_delay) {
//ngx_accept_mutex_delay 当获得锁失败后,再次去请求锁的间隔时间
timer = ngx_accept_mutex_delay;
}
}
}
}
//#define ngx_process_events ngx_event_actions.process_events
//In epoll, ngx_event_actions = ngx_epoll_module_ctx.actions;
//全局变量ngx_epoll_module_ctx(类型ngx_event_module_t),内有actions(类型ngx_event_actions_t),定义process_events钩子
//钩子调用epoll的ngx_epoll_process_events()
(void) ngx_process_events(cycle, timer, flags);
delta = ngx_current_msec - delta;//计算process所用时间
//ngx_posted_accept_events队列不为空-->有accept事件发生,就去处理
//accept事件,其实最后就是调用accept函数接收新的连接
//rev->handler = ngx_event_accept在ngx_event_process_init里面设置
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();
}
//除了accept事件之外的其他事件放在这个队列中,如果队列不为空,就去处理相关的事件
if (ngx_posted_events) {
//一般不用线程来处理
if (ngx_threaded) {
ngx_wakeup_worker_thread(cycle);
} else {
ngx_event_process_posted(cycle, &ngx_posted_events);
}
}
}
说到这里不得不提一下一个很重要的结构ngx_module_t,这里面定义了一些非常重要的函数钩子:
struct ngx_module_s {
//ctx_index是分类的模块计数器,nginx的模块可以分为四种:core、event、http和
//mail,每一种的模块又会各自计数一下,这个ctx_index就是每个模块在其所属类组的计数值
ngx_uint_t ctx_index;
//index是一个模块计数器,按照每个模块在ngx_modules[]数组中的声明顺序
//(见objs/ngx_modules.c),从0开始依次给每个模块进行编号
ngx_uint_t index;
/*...*/
ngx_uint_t version;
//ctx是模块的上下文,不同种类的模块有不同的上下文,四类模块就有四种模块上下文,实现为四个不同的结构体,所以ctx是void *
void *ctx;
//commands 是模块的指令集,nginx的每个模块都可以实现一些自定义的指令,这些指令
//写在配置文件的适当配置项中,每一个指令在源码中对应着一个 ngx_command_t结构的
//变量,nginx会从配置文件中把模块的指令读取出来放到模块的commands指令数组中,
//这些指令一般是把配置项的 参数值赋给一些程序中的变量或者是在不同的变量之间合并或
//转换数据(例如include指令),指令可以带参数也可以不带参数,你可以把这些指令想象
//为 unix的命令行或者是一种模板语言的指令。
ngx_command_t *commands;
//type就是模块的种类,前面已经说过,nginx模块分为core、event、http和mail四类,type用宏定义标识四个分类。
ngx_uint_t type;
//init_master、 init_module、init_process、init_thread、exit_thread、
//exit_process、 exit_master是函数指针,指向模块实现的自定义回调函数,
//这些回调函数分别在初始化master、初始化模块、初始化工作进程、初始化线程、
//退出线程、退出工作进程和退出master的时候被调用,如果模块需要在这些时机做处理,
//就可以实现对应的函数,并把它赋值给对应的函数指针来注册一个回调 函数接口
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);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
/*...*/
};
//epoll处理事件的函数
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
//得到发生的事件表event_list
events = epoll_wait(ep, event_list, (int) nevents, timer);
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
ngx_time_update();
}
//上锁ngx_posted_events_mutex
//ngx_posted_events_mutex只有在NGX_THREAD宏定义有效时才有效
ngx_mutex_lock(ngx_posted_events_mutex);
for (i = 0; i < events; i++) {
//获取事件的connection
c = event_list[i].data.ptr;
instance = (uintptr_t) c & 1;
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
rev = c->read;
revents = event_list[i].events;
if ((revents & (EPOLLERR|EPOLLHUP)) &&
(revents & (EPOLLIN|EPOLLOUT)) == 0) {
/*
* if the error events were returned without EPOLLIN or EPOLLOUT,
* then add these flags to handle the events at least in one active handler
*/
revents |= EPOLLIN|EPOLLOUT;
}
//default rev->active is 1
if ((revents & EPOLLIN) && rev->active) {
if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
rev->posted_ready = 1;
}
}
else {
rev->ready = 1;
}
if (flags & NGX_POST_EVENTS) {
//如果是新的连接,accept就会被设为1;accept()之后还没有断开(timeout),accept就是0
//这个步骤不处理连接,只是把连接放在queue(ngx_posted_accept_events或者ngx_posted_events)里面
queue = (ngx_event_t **) (rev->accept ? &ngx_posted_accept_events : &ngx_posted_events);
ngx_locked_post_event(rev, queue);
}
else {
rev->handler(rev);
}
wev = c->write;
if ((revents & EPOLLOUT) && wev->active) {
if (flags & NGX_POST_THREAD_EVENTS) {
wev->posted_ready = 1;
}
else {
wev->ready = 1;
}
if (flags & NGX_POST_EVENTS) {
ngx_locked_post_event(wev, &ngx_posted_events);
}
else {
wev->handler(wev);
}
}
//解锁
ngx_mutex_unlock(ngx_posted_events_mutex);
return NGX_OK;
}