我对网络IO的理解( 三 )


ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高 。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死 。
详细对比

我对网络IO的理解

文章插图
三种I/O多路复用对比
Nginx中Epoll+非阻塞IO
 Nginx高并发主要是通过Epoll模式+非阻塞I/O
Nginx对I/O多路复用进行封装,封装在结构体struct ngx_event_s,同时将事件封装在ngx_event_actions_t结构中 。
typedef struct { ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags); ngx_int_t (*add_conn)(ngx_connection_t *c); ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags); ngx_int_t (*notify)(ngx_event_handler_pt handler); 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;初始化epoll句柄
static ngx_int_tngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer){ ngx_epoll_conf_t *epcf; epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module); if (ep == -1) { ep = epoll_create(cycle->connection_n / 2); if (ep == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "epoll_create() failed"); return NGX_ERROR; } ... }}将fd设置为非阻塞
(ngx_nonblocking(s) == -1) #nginx将fd设置非阻塞设置事件触发
static ngx_int_tngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags){ int op; uint32_t events, prev; ngx_event_t *e; ngx_connection_t *c; struct epoll_event ee; c = ev->data; events = (uint32_t) event; if (event == NGX_READ_EVENT) { e = c->write; prev = EPOLLOUT;#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP) events = EPOLLIN|EPOLLRDHUP;#endif } else { e = c->read; prev = EPOLLIN|EPOLLRDHUP;#if (NGX_WRITE_EVENT != EPOLLOUT) events = EPOLLOUT;#endif } if (e->active) { op = EPOLL_CTL_MOD; events |= prev; } else { op = EPOLL_CTL_ADD; }#if (NGX_HAVE_EPOLLEXCLUSIVE && NGX_HAVE_EPOLLRDHUP) if (flags & NGX_EXCLUSIVE_EVENT) { events &= ~EPOLLRDHUP; }#endif ee.events = events | (uint32_t) flags; ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, "epoll add event: fd:%d op:%d ev:%08XD", c->fd, op, ee.events); if (epoll_ctl(ep, op, c->fd, &ee) == -1) { ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "epoll_ctl(%d, %d) failed", op, c->fd); return NGX_ERROR; } ev->active = 1;#if 0 ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;#endif return NGX_OK;}处理就绪的事件
static ngx_int_tngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags){ int events; uint32_t revents; ngx_int_t instance, i; ngx_uint_t level; ngx_err_t err; ngx_event_t *rev, *wev; ngx_queue_t *queue; ngx_connection_t *c; /* NGX_TIMER_INFINITE == INFTIM */ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "epoll timer: %M", timer); events = epoll_wait(ep, event_list, (int) nevents, timer); ...} 引用
深入解读同步/异步 IO 编程模型
关于同步/异步 VS 阻塞/非阻塞的一点体会
怎样理解阻塞非阻塞与同步异步的区别?




推荐阅读