Nginx源码分析-Epoll模块

来源:互联网 发布:网络知识竞赛的网址 编辑:程序博客网 时间:2024/05/17 03:45

 

转载自:http://blog.csdn.net/marcky/article/details/6069958

 

Linux平台上,Nginx使用epoll完成事件驱动,实现高并发;本文将不对epoll本身进行介绍(网上一堆一堆的文章介绍epoll的原理及使用方法,甚至源码分析等),仅看一下Nginx是如何使用epoll的。

 

Nginx在epoll模块中定义了好几个函数,这些函数基本都是作为回调注册到事件抽象层的对应接口上,从而实现了事件驱动的具体化,我们看如下的一段代码:

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

 

 

这段代码就是epoll的相关函数注册到事件抽象层,这里所谓的事件抽象层在前面的博文中有提过,就是Nginx为了方便支持和开发具体的I/O模型,从而实现的一层抽象。代码后面的注释将功能说明得很详细了,本文就只重点关注ngx_epoll_init和ngx_epoll_process_events两个函数,其他几个函数就暂且忽略了。


 

 

ngx_epoll_init主要是完成epoll的相关初始化工作,代码分析如下:

[cpp] view plaincopyprint?
  1. static ngx_int_t  
  2. ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)  
  3. {  
  4.     ngx_epoll_conf_t  *epcf;  
  5.     /*取得epoll模块的配置结构*/  
  6.     epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);  
  7.     /*ep是epoll模块定义的一个全局变量,初始化为-1*/  
  8.     if (ep == -1) {  
  9.         /*创一个epoll对象,容量为总连接数的一半*/  
  10.         ep = epoll_create(cycle->connection_n / 2);  
  11.         if (ep == -1) {  
  12.             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,  
  13.                           "epoll_create() failed");  
  14.             return NGX_ERROR;  
  15.         }     
  16.     }  
  17.     /*nevents也是epoll模块定义的一个全局变量,初始化为0*/  
  18.     if (nevents < epcf->events) {  
  19.         if (event_list) {  
  20.             ngx_free(event_list);  
  21.         }  
  22.           
  23.         /*event_list存储产生事件的数组*/  
  24.         event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,  
  25.                                cycle->log);  
  26.         if (event_list == NULL) {  
  27.             return NGX_ERROR;  
  28.         }  
  29.     }  
  30.     nevents = epcf->events;  
  31.     /*初始化全局变量ngx_io, ngx_os_is定义为: 
  32.         ngx_os_io_t ngx_os_io = { 
  33.             ngx_unix_recv, 
  34.             ngx_readv_chain, 
  35.             ngx_udp_unix_recv, 
  36.             ngx_unix_send, 
  37.             ngx_writev_chain, 
  38.             0 
  39.         };(位于src/os/unix/ngx_posix_init.c) 
  40.     */  
  41.     ngx_io = ngx_os_io;  
  42.     /*这里就是将epoll的具体接口函数注册到事件抽象层接口ngx_event_actions上。 
  43.     具体是上文提到的ngx_epoll_module_ctx中封装的如下几个函数 
  44.         ngx_epoll_add_event,   
  45.         ngx_epoll_del_event,        
  46.         ngx_epoll_add_event,           
  47.         ngx_epoll_del_event,          
  48.         ngx_epoll_add_connection,         
  49.         ngx_epoll_del_connection,       
  50.         ngx_epoll_process_events,        
  51.         ngx_epoll_init,                
  52.         ngx_epoll_done,           
  53.     */  
  54.     ngx_event_actions = ngx_epoll_module_ctx.actions;  
  55. #if (NGX_HAVE_CLEAR_EVENT)  
  56.     /*epoll将添加这个标志,主要为了实现边缘触发*/  
  57.     ngx_event_flags = NGX_USE_CLEAR_EVENT  
  58. #else   
  59.     /*水平触发*/  
  60.     ngx_event_flags = NGX_USE_LEVEL_EVENT  
  61. #endif   
  62.                       |NGX_USE_GREEDY_EVENT /*io的时候,直到EAGAIN为止*/  
  63.                       |NGX_USE_EPOLL_EVENT; /*epoll标志*/  
  64.     return NGX_OK;  
  65. }  

 

epoll初始化工作没有想象中的复杂,和我们平时使用epoll都一样,下面看ngx_epoll_process_events,这个函数主要用来完成事件的等待并处理。

[cpp] view plaincopyprint?
  1. static ngx_int_t  
  2. ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)  
  3. {  
  4.     int                events;  
  5.     uint32_t           revents;  
  6.     ngx_int_t          instance, i;  
  7.     ngx_uint_t         level;  
  8.     ngx_err_t          err;  
  9.     ngx_log_t         *log;  
  10.     ngx_event_t       *rev, *wev, **queue;  
  11.     ngx_connection_t  *c;  
  12.     /*一开始就是等待事件,最长等待时间为timer;nginx为事件 
  13.     专门用红黑树维护了一个计时器。后续对这个timer单独分析。 
  14.     */  
  15.     events = epoll_wait(ep, event_list, (int) nevents, timer);  
  16.     if (events == -1) {  
  17.         err = ngx_errno;  
  18.     } else {  
  19.         err = 0;  
  20.     }  
  21.     if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {  
  22.         /*执行一次时间更新, nginx将时间缓存到了一组全局变量中,方便程序高效的获取事件。*/  
  23.         ngx_time_update();  
  24.     }  
  25.     /*处理wait错误*/  
  26.     if (err) {  
  27.         if (err == NGX_EINTR) {  
  28.             if (ngx_event_timer_alarm) {  
  29.                 ngx_event_timer_alarm = 0;  
  30.                 return NGX_OK;  
  31.             }  
  32.             level = NGX_LOG_INFO;  
  33.         } else {  
  34.             level = NGX_LOG_ALERT;  
  35.         }  
  36.         ngx_log_error(level, cycle->log, err, "epoll_wait() failed");  
  37.         return NGX_ERROR;  
  38.     }  
  39.     /*wait返回事件数0,可能是timeout返回,也可能是非timeout返回;非timeout返回则是error*/  
  40.     if (events == 0) {  
  41.         if (timer != NGX_TIMER_INFINITE) {  
  42.             return NGX_OK;  
  43.         }  
  44.         ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,  
  45.                       "epoll_wait() returned no events without timeout");  
  46.         return NGX_ERROR;  
  47.     }  
  48.     log = cycle->log;  
  49.     /*for循环开始处理收到的所有事件*/  
  50.     for (i = 0; i < events; i++) {  
  51.       
  52.         /*取得发生此事件的连接*/  
  53.         c = event_list[i].data.ptr;  
  54.         instance = (uintptr_t) c & 1;  
  55.         c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);  
  56.         /*获得该连接上的读事件*/  
  57.         rev = c->read;  
  58.         。。。。。。。。。。。。。  
  59.           
  60.         /*取得发生一个事件*/  
  61.         revents = event_list[i].events;  
  62.       
  63.         /*记录wait的错误返回状态*/  
  64.         if (revents & (EPOLLERR|EPOLLHUP)) {  
  65.             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,  
  66.                            "epoll_wait() error on fd:%d ev:%04XD",  
  67.                            c->fd, revents);  
  68.         }  
  69.         if ((revents & (EPOLLERR|EPOLLHUP))  
  70.              && (revents & (EPOLLIN|EPOLLOUT)) == 0)  
  71.         {  
  72.             /* 
  73.              * if the error events were returned without EPOLLIN or EPOLLOUT, 
  74.              * then add these flags to handle the events at least in one 
  75.              * active handler 
  76.              */  
  77.             revents |= EPOLLIN|EPOLLOUT;  
  78.         }  
  79.         /*该事件是一个读事件,并该连接上注册的读事件是active的*/  
  80.         if ((revents & EPOLLIN) && rev->active) {  
  81.             if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {  
  82.                 rev->posted_ready = 1;  
  83.             } else {  
  84.                 rev->ready = 1;  
  85.             }  
  86.               
  87.             /*事件放入相应的队列中;关于此处的先入队再处理,在前面的文章中已经介绍过了。*/  
  88.             if (flags & NGX_POST_EVENTS) {  
  89.                 queue = (ngx_event_t **) (rev->accept ?  
  90.                                &ngx_posted_accept_events : &ngx_posted_events);  
  91.                 ngx_locked_post_event(rev, queue); /*入队*/  
  92.             } else {  
  93.                 rev->handler(rev);  
  94.             }  
  95.         }  
  96.         wev = c->write;  
  97.         /*发生的是一个写事件,和读事件完全一样的逻辑过程*/  
  98.         if ((revents & EPOLLOUT) && wev->active) {  
  99.             if (flags & NGX_POST_THREAD_EVENTS) {  
  100.                 wev->posted_ready = 1;  
  101.             } else {  
  102.                 wev->ready = 1;  
  103.             }  
  104.             /*先入队再处理*/  
  105.             if (flags & NGX_POST_EVENTS) {  
  106.                 ngx_locked_post_event(wev, &ngx_posted_events);  
  107.             } else {  
  108.                 wev->handler(wev);  
  109.             }  
  110.         }  
  111.     }  
  112.     return NGX_OK;  
  113. }  

 

 

本文将关注的两个epoll函数也就这么一点代码了,但整个epoll还有添加事件和删除事件等的相关函数,代码都很简单,本文就不做具体的分析了。

 

写到此处的时候,我感觉epoll模块没有分析的足够详细,或者说是没有足够的理解作者的用意,如果你有更好的理解,希望能够告诉我。或许,随着后面的分析,能够逐渐的真正明白吧。

 

0 0