nginx 源码学习笔记(二十二)—— event 模块(三) ——epoll模块

上一节我们讲到了事件驱动的模块,它把我们引入epoll模块,今天我们主要学习下nginx如何使用epoll完成时间驱动,实现高并发;这里不详细讲解epoll原理,如果有机会再做一次单独的epoll的学习。

本文来自于:http://blog.csdn.net/lengzijian

回忆一下上一节的内容,在我们讲到ngx_process_events_and_timers时,在源码最后提到了ngx_process_events,这里是把我们引入epoll的入口:

1.先来看下ngx_process_events的宏定义:

  1. src/event/ngx_event.h  
  2.   
  3. #define ngx_process_events   ngx_event_actions.process_events  


2.继续查找ngx_event_actions,我们找到如下结构体:

  1. src/event/ngx_event.h  
  2.   
  3. typedef struct {  
  4.     ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);  
  5.     ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);  
  6.   
  7.     ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);  
  8.     ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);  
  9.   
  10.     ngx_int_t  (*add_conn)(ngx_connection_t *c);  
  11.     ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);  
  12.   
  13.     ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);  
  14.     ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,  
  15.                    ngx_uint_t flags);  
  16.   
  17.     ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);  
  18.     void       (*done)(ngx_cycle_t *cycle);  
  19. } ngx_event_actions_t;  


a.我们去源代码中搜索下关键字ngx_event_actions

  1. modules/ngx_epoll_module.c:    ngx_event_actions = ngx_epoll_module_ctx.actions;  
  2. modules/ngx_select_module.c:    ngx_event_actions = ngx_select_module_ctx.actions;  
  3. modules/ngx_poll_module.c:    ngx_event_actions = ngx_poll_module_ctx.actions;  
  4.   
  5. ngx_event.c:ngx_event_actions_t   ngx_event_actions;  

前面三行表示:所有event模块对象中的actions就是ngx_event_actions_t对象,而ngx_event_action在第四行定义为全局变量,用于同一接口,下面又存在一个疑问,event模块到底做了些什么?

b.先找到ngx_event_module_t的结构体:

  1. src/event/ngx_event.h  
  2.   
  3. typedef struct {  
  4.     ngx_str_t              *name;           //模块名  
  5.   
  6.     void                 *(*create_conf)(ngx_cycle_t *cycle);  //钩子函数,之前讲过  
  7.     char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);//同上  
  8.   
  9.     ngx_event_actions_t     actions;       //接下来主要看  
  10. } ngx_event_module_t;  


我们找一个例子来详细讲解下

  1. src/event/modules/ngx_epoll_module.c  
  2.   
  3. ngx_event_module_t  ngx_epoll_module_ctx = {  
  4.     &epoll_name,  
  5.     ngx_epoll_create_conf,               /* create configuration */  
  6.     ngx_epoll_init_conf,                 /* init configuration */  
  7.   
  8.     {  
  9.         ngx_epoll_add_event,             /* add an event */  
  10.         ngx_epoll_del_event,             /* delete an event */  
  11.         ngx_epoll_add_event,             /* enable an event */  
  12.         ngx_epoll_del_event,             /* disable an event */  
  13.         ngx_epoll_add_connection,        /* add an connection */  
  14.         ngx_epoll_del_connection,        /* delete an connection */  
  15.         NULL,                            /* process the changes */  
  16.         ngx_epoll_process_events,        /* process the events */  
  17.         ngx_epoll_init,                  /* init the events */  
  18.         ngx_epoll_done,                  /* done the events */  
  19.     }  
  20. };  

这里有注释就不详细讲解了。

ngx_process_events这个函数就是我们要找的,要了好大一圈,ngx_process_events实际上就是调用这个函数,此处本人纠结,为什么作者不加点注释呢。

3.下面正式观察下ngx_epoll_init函数:

  1. src/event/modules/ngx_epoll_module.c  
  2.   
  3. static ngx_int_t  
  4. ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)  
  5. {  
  6.     ngx_epoll_conf_t  *epcf;  
  7.     //获取epoll模块的配置结构  
  8.     epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);  
  9.     //ep是epoll模块定义的一个全局变量,初始化为-1  
  10.     if (ep == -1) {  
  11.         //创建一个epoll对象,容量为总连接数的一半  
  12.         ep = epoll_create(cycle->connection_n / 2);  
  13.   
  14.         if (ep == -1) {  
  15.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,  
  16.                           "epoll_create() failed");  
  17.             return NGX_ERROR;  
  18.         }  
  19.     }  
  20.     //nevents也是epoll模块的全局变量,初始化为0  
  21.     if (nevents < epcf->events) {  
  22.         if (event_list) {  
  23.             ngx_free(event_list);  
  24.         }  
  25.         //event_list存储产生时间的数组  
  26.         event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,  
  27.                                cycle->log);  
  28.         if (event_list == NULL) {  
  29.             return NGX_ERROR;  
  30.         }  
  31.     }  
  32.   
  33.     nevents = epcf->events;  
  34.     /*初始化全局变量ngx_io,ngx_os_io定义为: 
  35.     ngx_os_io_t ngx_os_io = { 
  36.         ngx_unix_recv, 
  37.         ngx_readv_chain, 
  38.         ngx_udp_unix_recv, 
  39.         ngx_unix_send, 
  40.         ngx_writev_chain, 
  41.         0 
  42.     };(src/os/unix/ngx_posix_init.c) 
  43.     */  
  44.     ngx_io = ngx_os_io;  
  45.     //这里之前讲过  
  46.     ngx_event_actions = ngx_epoll_module_ctx.actions;  
  47.   
  48. #if (NGX_HAVE_CLEAR_EVENT)  
  49.     //实现边沿触发  
  50.     ngx_event_flags = NGX_USE_CLEAR_EVENT  
  51. #else  
  52.     //实现水平出发  
  53.     ngx_event_flags = NGX_USE_LEVEL_EVENT  
  54. #endif  
  55.                       |NGX_USE_GREEDY_EVENT  //io是知道收到DAGAIN为止  
  56.                       |NGX_USE_EPOLL_EVENT;  //epoll标志  
  57.   
  58.     return NGX_OK;  
  59. }  


4.下面观察下主要的函数ngx_epoll_process_events:

  1. src/event/modules/ngx_epoll_module.c  
  2.   
  3. static ngx_int_t  
  4. ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)  
  5. {  
  6.     int                events;  
  7.     uint32_t           revents;  
  8.     ngx_int_t          instance, i;  
  9.     ngx_uint_t         level;  
  10.     ngx_err_t          err;  
  11.     ngx_event_t       *rev, *wev, **queue;  
  12.     ngx_connection_t  *c;  
  13.   
  14.     /* NGX_TIMER_INFINITE == INFTIM */  
  15.   
  16.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,  
  17.                    "epoll timer: %M", timer);  
  18.     //一开支就等待时间,最长等待时间为timer,这里的timer下一节会详细讲解  
  19.     events = epoll_wait(ep, event_list, (int) nevents, timer);  
  20.   
  21.     err = (events == -1) ? ngx_errno : 0;  
  22.   
  23.     if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {  
  24.         //执行一次时间更新,nginx将时间缓存到一组全局变量中,方便程序高效获取事件  
  25.         ngx_time_update();  
  26.     }  
  27.     //wait出错  
  28.     if (err) {  
  29.         if (err == NGX_EINTR) {  
  30.   
  31.             if (ngx_event_timer_alarm) {  
  32.                 ngx_event_timer_alarm = 0;  
  33.                 return NGX_OK;  
  34.             }  
  35.   
  36.             level = NGX_LOG_INFO;  
  37.   
  38.         } else {  
  39.             level = NGX_LOG_ALERT;  
  40.         }  
  41.   
  42.         ngx_log_error(level, cycle->log, err, "epoll_wait() failed");  
  43.         return NGX_ERROR;  
  44.     }  
  45.     //wait返回事件数0,可能是timeout返回,如果不是timeout返回,那么就是error  
  46.     if (events == 0) {  
  47.         //这里限定timer不是无线超时  
  48.         if (timer != NGX_TIMER_INFINITE) {  
  49.             return NGX_OK;  
  50.         }  
  51.   
  52.         ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,  
  53.                       "epoll_wait() returned no events without timeout");  
  54.         return NGX_ERROR;  
  55.     }  
  56.     //ngx_posted_events_mutex上锁  
  57.     ngx_mutex_lock(ngx_posted_events_mutex);  
  58.     //  
  59.     for (i = 0; i < events; i++) {  
  60.         c = event_list[i].data.ptr;  
  61.   
  62.         instance = (uintptr_t) c & 1;  
  63.         //从发生的epoll事件对象中取得ngx_connection_t对象  
  64.         c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);  
  65.         //取出读事件  
  66.         rev = c->read;  
  67.         //....  
  68.         //取得发生的时间  
  69.         revents = event_list[i].events;  
  70.         //记录wait的错误返回状态  
  71.         if (revents & (EPOLLERR|EPOLLHUP)) {  
  72.             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,  
  73.                            "epoll_wait() error on fd:%d ev:%04XD",  
  74.                            c->fd, revents);  
  75.         }  
  76.           
  77.         if ((revents & (EPOLLERR|EPOLLHUP))  
  78.              && (revents & (EPOLLIN|EPOLLOUT)) == 0)  
  79.         {  
  80.             /* 
  81.              * if the error events were returned without EPOLLIN or EPOLLOUT, 
  82.              * then add these flags to handle the events at least in one 
  83.              * active handler 
  84.              */  
  85.   
  86.             revents |= EPOLLIN|EPOLLOUT;  
  87.         }  
  88.         //读取一个事件,并且该连接上注册的读时间是active的  
  89.         if ((revents & EPOLLIN) && rev->active) {  
  90.             if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {  
  91.                 rev->posted_ready = 1;  
  92.   
  93.             } else {  
  94.                 rev->ready = 1;  
  95.             }  
  96.             //时间放入相应的队列中;之前有说过nginx是先放入队列,释放锁之后再做处理  
  97.             if (flags & NGX_POST_EVENTS) {  
  98.                 queue = (ngx_event_t **) (rev->accept ?  
  99.                                &ngx_posted_accept_events : &ngx_posted_events);  
  100.                 /* 
  101.                 这里根据accept状态 
  102.                 如果accept为真:加入到ngx_posted_accept_events事件队列中 
  103.                 如果accept为假:加入到ngx_posted_events事件队列中 
  104.                 */  
  105.                 ngx_locked_post_event(rev, queue);//加入到ngx_posted_accept_events队里面  
  106.   
  107.             } else {  
  108.                 rev->handler(rev);//调用读事件处理函数,通常就是读取事件了  
  109.             }  
  110.         }  
  111.         //取出写事件  
  112.         wev = c->write;  
  113.         //如果是写事件并且active状态  
  114.         if ((revents & EPOLLOUT) && wev->active) {  
  115.           
  116.             if (c->fd == -1 || wev->instance != instance) {  
  117.   
  118.                 /* 
  119.                  * the stale event from a file descriptor 
  120.                  * that was just closed in this iteration 
  121.                  */  
  122.   
  123.                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,  
  124.                                "epoll: stale event %p", c);  
  125.                 continue;  
  126.             }  
  127.   
  128.             if (flags & NGX_POST_THREAD_EVENTS) {  
  129.                 //写事件设为posted_ready状态  
  130.                 wev->posted_ready = 1;  
  131.   
  132.             } else {  
  133.                 写事件设为ready状态  
  134.                 wev->ready = 1;  
  135.             }  
  136.   
  137.             if (flags & NGX_POST_EVENTS) {  
  138.                 //不立即处理,要时间排队,加入队列  
  139.                 ngx_locked_post_event(wev, &ngx_posted_events);  
  140.   
  141.             } else {  
  142.                 //调用写事件处理函数,通常就是写入数据  
  143.                 wev->handler(wev);  
  144.             }  
  145.         }  
  146.     }  
  147.     //ngx_mutex_unlock解锁  
  148.     ngx_mutex_unlock(ngx_posted_events_mutex);  
  149.   
  150.     return NGX_OK;  
  151. }  
  152. //其中用到了ngx_locked_post_event()这个宏,它把事件放到事件队列的头部。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值