您的位置:首页 > 运维架构 > Nginx

Nginx源代码分析之I/O模型细节(七)

2015-07-10 20:16 507 查看
这里谈谈源码的具体实现

作为统一框架,每个模型都有统一的处理接口包括,这里定义在

typedef struct {

ngx_int_t (*add)(ngx_event_t *ev, int event, u_int flags);

ngx_int_t (*del)(ngx_event_t *ev, int event, u_int flags);

ngx_int_t (*enable)(ngx_event_t *ev, int event, u_int flags);

ngx_int_t (*disable)(ngx_event_t *ev, int event, u_int flags);

ngx_int_t (*add_conn)(ngx_connection_t *c);

ngx_int_t (*del_conn)(ngx_connection_t *c, u_int flags);

ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);

ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,

ngx_uint_t flags);

ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);

void (*done)(ngx_cycle_t *cycle);

} ngx_event_actions_t;

可以看到,主要有add,del,process_events,init,done,等主要方法。有点类似OOB的多态,定义了统一的抽象接口。

于是每个模型都有自己的具体实现:

比如iocp模型下,分别实现了ngx_iocp_add_event,ngx_iocp_process_events,ngx_iocp_init

epoll模型下,则实现了ngx_epoll_add_event,ngx_epoll_del_event,ngx_epoll_process_events,ngx_epoll_init

select则与epoll类似。

我们知道,ngx_single_process_cycle是I/O处理的线程,其中,process_events 方法是在这个线程中被调用。

for ( ;; ) {

ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

ngx_process_events_and_timers(cycle);

if (ngx_terminate || ngx_quit) {

for (i = 0; ngx_modules[i]; i++) {

if (ngx_modules[i]->exit_process) {

ngx_modules[i]->exit_process(cycle);

}

}

ngx_master_process_exit(cycle);

}

if (ngx_reconfigure) {

ngx_reconfigure = 0;

ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");

cycle = ngx_init_cycle(cycle);

if (cycle == NULL) {

cycle = (ngx_cycle_t *) ngx_cycle;

continue;

}

ngx_cycle = cycle;

}

if (ngx_reopen) {

ngx_reopen = 0;

ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");

ngx_reopen_files(cycle, (ngx_uid_t) -1);

}

实际上是在ngx_process_events_and_timers函数中调用处理方法,ngx_process_events_and_timers函数涉及到多个重要问题。比如accept的互斥管理的问题,超时的管理问题,不同I/O模型的ngx_process_events的处理,ngx_posted_accept_events和ngx_posted_events队列的处理。

这里只谈谈ngx_process_events。 ngx_process_events主要用于异步模型中当时未完成,但延迟完成的情况。

对于每个模型来说,无非是通过各模型的API,读取I/O的状态,来判断是否有I/O数据到达并就绪。每个模型的代码细节就不谈了,谈谈代码上的一些关键点以及一切差别。

关于timer,每个模型各有特点,但都会设置这个参数。比如GetQueuedCompletionStatus的最后一个参数用来设置timer。

就绪I/O的处理方法各有不同,对于select,会检查work_write_fd_set和work_read_fd_set的每个一句柄,然后将可读可写的socket对应的ev放入accept队列或者普通队列中。

代码如下:

for (i = 0; i < nevents; i++) {

ev = event_index[i];

c = ev->data;

found = 0;

if (ev->write) {

if (FD_ISSET(c->fd, &work_write_fd_set)) {

found = 1;

ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

"select write %d", c->fd);

}

} else {

if (FD_ISSET(c->fd, &work_read_fd_set)) {

found = 1;

ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

"select read %d", c->fd);

}

}

if (found) {

ev->ready = 1;

queue = ev->accept ? &ngx_posted_accept_events

: &ngx_posted_events;

ngx_post_event(ev, queue);

nready++;

}

}

然后接着调用ngx_event_process_posted(cycle, &ngx_posted_accept_events);以及ngx_event_process_posted(cycle, &ngx_posted_events);来处理已完成队列。

而iocp则不会使用这2个数据结构,在ngx_process_events_and_timers中,也就是ngx_iocp_process_events函数,iocp会直接调用ev->handler(ev);完成回调处理。

对于epoll模型,如果存在NGX_POST_EVENTS标志,会跟select一样把就绪I/O放入2个对之列中,如果不存在,则直接调用rev->handler(rev);

为什么有这样的差别,以后在详细论述。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: