参考资料<深入理解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参数的意义如下表

Nginx:事件模块

参数fd是待检测的连接套接字,第四个参数是在告诉epoll对什么样的事件感兴趣,它使用的epoll_event结构体定义如下

struct epoll_event {
    _uint32_t events;
    epoll_data_t data;
};

   events取值如下表

Nginx:事件模块

  而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 }
View Code

相关文章: