参考资料<深入理解Nginx>
根据不同的系统内核,Nginx会使用不同的事件驱动机制,本次描述的场景是使用epoll来驱动事件的处理。
epoll的使用方法
1.int epoll_create(int size);
epoll_create返回一个句柄,之后epoll的使用将依靠这个句柄来标识。参数size只是告诉epoll所要处理的大致事件数目,一些内核版本的实现中,这个参数没有任何意义。
2.int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
epoll_ctl向epoll对象中添加、修改或者删除感兴趣的事件,返回0表示成功,否则返回-1。
参数epfd是epoll_create方法返回的句柄,而op参数的意义如下表
参数fd是待检测的连接套接字,第四个参数是在告诉epoll对什么样的事件感兴趣,它使用的epoll_event结构体定义如下
struct epoll_event { _uint32_t events; epoll_data_t data; };
events取值如下表
而data成员是一个epoll_data联合
typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t;
这个data成员还与具体的使用方式相关。例如,ngx_epoll_module模块使用了给ptr成员,作为指向ngx_connection_t连接的指针。
3.int epoll_wait(int fd,struct epoll_event *events,int maxevents,int timeout);
第一个参数epfd是epoll的描述符。第二个参数events则是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中,
第三个参数表示本次可以返回的最大事件数目,第四个参数表示在没有检测到时间发生时最多等待的时间。
ngx_epoll_module模块
该模块使用如下的上下文结构
ngx_event_module_t ngx_epoll_module_ctx = { &epoll_name, ngx_epoll_create_conf, /* create configuration */ ngx_epoll_init_conf, /* init configuration */ { ngx_epoll_add_event, /* add an event */ ngx_epoll_del_event, /* delete an event */ ngx_epoll_add_event, /* enable an event */ ngx_epoll_del_event, /* disable an event */ ngx_epoll_add_connection, /* add an connection */ ngx_epoll_del_connection, /* delete an connection */ NULL, /* process the changes */ ngx_epoll_process_events, /* process the events */ ngx_epoll_init, /* init the events */ ngx_epoll_done, /* done the events */ } };
在Nginx的启动过程中。ngx_epoll_init方法将会被调用,它主要做了两件事情:
1.调用epoll_create方法创建epoll对象。
2.创建event_list数组,用于进行epoll_wait调用时传递内核态的事件。
ngx_epoll_add_event通过调用epoll_ctl向epoll中添加或者删除事件。可以通过这个函数的部分源码来了解一下该过程
1 static ngx_int_t 2 ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) 3 { 4 int op; 5 uint32_t events, prev; 6 ngx_event_t *e; 7 ngx_connection_t *c; 8 struct epoll_event ee; 9 10 //每个事件的data成员都存放着其对应的ngx_connection_t连接 11 c = ev->data; 12 13 //确定当前时间是读事件还是写事件 14 events = (uint32_t) event; 15 16 ... 17 //根据active标志位确定是否为活跃事件,以决定到底是修改还是添加事件 18 if (e->active) { 19 op = EPOLL_CTL_MOD; 20 events |= prev; 21 22 } else { 23 op = EPOLL_CTL_ADD; 24 } 25 26 ee.events = events | (uint32_t) flags; 27 ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); 28 29 //调用epoll_ctl方法向epoll中添加事件 30 if (epoll_ctl(ep, op, c->fd, &ee) == -1) { 31 ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, 32 "epoll_ctl(%d, %d) failed", op, c->fd); 33 return NGX_ERROR; 34 } 35 36 //标识为活跃事件 37 ev->active = 1; 38 39 return NGX_OK; 40 }