Chinaunix首页 | 论坛 | 博客
  • 博客访问: 113730
  • 博文数量: 22
  • 博客积分: 596
  • 博客等级: 中士
  • 技术积分: 165
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-17 15:32
文章分类

全部博文(22)

文章存档

2012年(1)

2011年(20)

2010年(1)

分类: 系统运维

2011-08-07 19:23:42

Nginx的高性能应该算是事件驱动的功劳。Nginx事件处理的相关代码位于src/event目录中,事件驱动是Nginx的核心,所以代码量相对也比较大。事件驱动初始化的过程主要由下图中的三步组成。

 

 

第一步:解析配置文件的初始化

在Nginx的启动初始化过程中,将调用ngx_conf_parse()解析配置文件,此过程将遇到类似如下的配置项:

events {
      worker_connections  20480;
}

此处的events是一个block指令,在它下面还可以配置很多其他的指令,比如这里的worker_connections等。events下面可以配置的指令定义在数组ngx_event_core_commands(位于src/event/ngx_event.c文件中)中。每个指令都有自己对应的回调函数,events指令的回调函数是ngx_events_block()(位于src/events/ngx_event.c文件中),即在解析配置文件到events指令的时候,将调用此回调函数。此函数的大概分析如下: 

 

  1. static char *  
  2. ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  
  3. {  
  4.     char                 *rv;  
  5.     void               ***ctx;  
  6.     ngx_uint_t            i;  
  7.     ngx_conf_t            pcf;  
  8.     ngx_event_module_t   *m;  
  9.     /* 这个源码中的英文注释已经很清楚了。*/  
  10.     /* count the number of the event modules and set up their indices */  
  11.     ngx_event_max_module = 0;  
  12.     for (i = 0; ngx_modules[i]; i++) {  
  13.         if (ngx_modules[i]->type != NGX_EVENT_MODULE) {  
  14.             continue;  
  15.         }  
  16.         ngx_modules[i]->ctx_index = ngx_event_max_module++;  
  17.     }  
  18.     ctx = ngx_pcalloc(cf->pool, sizeof(void *));  
  19.     if (ctx == NULL) {  
  20.         return NGX_CONF_ERROR;  
  21.     }  
  22.     /*为每个事件模块分配一个指针,用以保存相应配置结构的地址。*/  
  23.     *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));  
  24.     if (*ctx == NULL) {  
  25.         return NGX_CONF_ERROR;  
  26.     }  
  27.     *(void **) conf = ctx;  
  28.     /* 循环调用每个事件模块的create_conf函数,创建配置结构*/  
  29.     for (i = 0; ngx_modules[i]; i++) {  
  30.         if (ngx_modules[i]->type != NGX_EVENT_MODULE) {  
  31.             continue;  
  32.         }  
  33.         m = ngx_modules[i]->ctx;  
  34.         if (m->create_conf) {  
  35.             (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);  
  36.             if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {  
  37.                 return NGX_CONF_ERROR;  
  38.             }  
  39.         }  
  40.     }  
  41.     pcf = *cf;  
  42.     cf->ctx = ctx;  
  43.     cf->module_type = NGX_EVENT_MODULE;  
  44.     cf->cmd_type = NGX_EVENT_CONF;  
  45.     /* 由于events是一个block指令,events域下还可以配置很多其他指令, 
  46.     比如use等,现在开始解析events block中的指令,完成初始化工作。*/  
  47.     rv = ngx_conf_parse(cf, NULL);  
  48.     *cf = pcf;  
  49.     if (rv != NGX_CONF_OK)  
  50.         return rv;  
  51.     /*循环执行每个事件模块的init_conf函数,初始化配置结构*/  
  52.     for (i = 0; ngx_modules[i]; i++) {  
  53.         if (ngx_modules[i]->type != NGX_EVENT_MODULE) {  
  54.             continue;  
  55.         }  
  56.         m = ngx_modules[i]->ctx;  
  57.         if (m->init_conf) {  
  58.             rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);  
  59.             if (rv != NGX_CONF_OK) {  
  60.                 return rv;  
  61.             }  
  62.         }  
  63.     }  
  64.     return NGX_CONF_OK;  
  65. }  

  1. static ngx_int_t  
  2. ngx_event_module_init(ngx_cycle_t *cycle)  
  3. {  
  4.     void              ***cf;  
  5.     u_char              *shared;  
  6.     size_t               size, cl;  
  7.     ngx_shm_t            shm;  
  8.     ngx_time_t          *tp;  
  9.     ngx_core_conf_t     *ccf;  
  10.     ngx_event_conf_t    *ecf;  
  11.       
  12.     cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);  
  13.     if (cf == NULL) {  
  14.         return NGX_ERROR;  
  15.     }  
  16.     /*取得ngx_event_core_module模块的配置结构*/  
  17.     ecf = (*cf)[ngx_event_core_module.ctx_index];  
  18.     /*取得ngx_core_module模块的配置结构*/      
  19.     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  
  20.     /*从ngx_core_module模块的配置结构中获取timer_resolution指令的配置参数*/  
  21.     ngx_timer_resolution = ccf->timer_resolution;  
  22.     。。。。。。。。。。。。。。。。。。。。  
  23.     /*如果关闭了master进程,就不需要下面的步骤了。因为,关闭了master进程就是 
  24.     单进程方式工作,下面的步骤是创建共享内存实现锁等工作,单进程不需要。 
  25.     */  
  26.     if (ccf->master == 0) {  
  27.         return NGX_OK;  
  28.     }  
  29.     /*已经存在accept互斥体了,不需要再重复创建*/  
  30.     if (ngx_accept_mutex_ptr) {  
  31.         return NGX_OK;  
  32.     }  
  33.     /* cl should be equal or bigger than cache line size */  
  34.     cl = 128;  
  35.     /*后面将会创建size大小的共享内存,这块共享内存将被均分成三段, 
  36.     分别供ngx_accept_mutex、ngx_connection_counter、ngx_temp_number 
  37.     使用。 
  38.     */  
  39.     size = cl            /* ngx_accept_mutex */  
  40.            + cl          /* ngx_connection_counter */  
  41.            + cl;         /* ngx_temp_number */  
  42.     。。。。。。。。。。。。。。。。。。。。。  
  43.     /*开始着手创建共享内存,大小为size,命名为nginx_shared_zone*/  
  44.     shm.size = size;  
  45.     shm.name.len = sizeof("nginx_shared_zone");  
  46.     shm.name.data = (u_char *) "nginx_shared_zone";  
  47.     shm.log = cycle->log;  
  48.     /*创建起共享内存,共享内存的起始地址保存在shm.addr*/  
  49.     if (ngx_shm_alloc(&shm) != NGX_OK) {  
  50.         return NGX_ERROR;  
  51.     }  
  52.     /*取得共享内存的起始地址*/  
  53.     shared = shm.addr;  
  54.       
  55.     /*accept互斥体取得共享内存的第一段cl大小内存*/  
  56.     ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;  
  57.       
  58.     /*创建accept互斥体。 
  59.      
  60.     accept互斥体的实现依赖是否支持原子操作,如果有相应的原子操作; 
  61.     就是用取得的这段共享内存来实现accept互斥体;否则,将使用文件 
  62.     锁来实现accept互斥体。 
  63.      
  64.     accept互斥体的作用是:避免惊群和实现worker进程的负载均衡。 
  65.     */  
  66.     if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data)  
  67.         != NGX_OK)  
  68.     {  
  69.         return NGX_ERROR;  
  70.     }  
  71.     /*ngx_connection_counter取得共享内存的第二段cl大小内存*/  
  72.     ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);  
  73.     (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);  
  74.       
  75.     /*ngx_temp_number取得共享内存的第三段cl大小内存*/  
  76.     ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);  
  77.     。。。。。。。。。。。。。。。。。。。。。。  
  78.     return NGX_OK;  
  79. }  

 

第三步:ngx_event_process_init

在worker进程的分析中,有提到调用每个模块自定义的进程初始化函数。ngx_event_process_init回调函数就是ngx_event_core_commands模块自定义的进程初始化函数。因此,在master进程创建好了一个worker进程后,worker进程首先就会做进程的初始化工作,此时就会调用ngx_event_process_init函数。ngx_event_process_init的代码分析如下:

  1. static ngx_int_t  
  2. ngx_event_process_init(ngx_cycle_t *cycle)  
  3. {  
  4.     ngx_uint_t           m, i;  
  5.     ngx_event_t         *rev, *wev;  
  6.     ngx_listening_t     *ls;  
  7.     ngx_connection_t    *c, *next, *old;  
  8.     ngx_core_conf_t     *ccf;  
  9.     ngx_event_conf_t    *ecf;  
  10.     ngx_event_module_t  *module;  
  11.     /*获取相应模块的配置结构*/  
  12.     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  
  13.     ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);  
  14.     /*master进程打开,worker进程数大于1,配置了accetp_mutex(默认使用) 
  15.     时,才使用accept互斥体。 
  16.     */  
  17.     if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {  
  18.         ngx_use_accept_mutex = 1;   /* 1 使用accept互斥体, 0 不使用*/  
  19.         ngx_accept_mutex_held = 0;  /* ngx_accept_mutex_held代表是否获得accept互斥体*/  
  20.         ngx_accept_mutex_delay = ecf->accept_mutex_delay; /*抢互斥体失败后,下次再抢的间隔时间*/  
  21.     } else {  
  22.         ngx_use_accept_mutex = 0;  
  23.     }  
  24.     /*初始化计时器,此处将会创建起一颗红黑色,来维护计时器。*/  
  25.     if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {  
  26.         return NGX_ERROR;  
  27.     }  
  28.     for (m = 0; ngx_modules[m]; m++) {  
  29.         if (ngx_modules[m]->type != NGX_EVENT_MODULE) {  
  30.             continue/*非NGX_EVENT_MODULE跳过*/  
  31.         }  
  32.         if (ngx_modules[m]->ctx_index != ecf->use) {  
  33.             continue;/*非use配置指令指定的模块跳过,Linux默认epoll*/  
  34.         }  
  35.         module = ngx_modules[m]->ctx;  
  36.         /*调用具体事件模块的init函数。 
  37.          
  38.         由于Nginx实现了很多的事件模块,比如:epoll,poll,select, kqueue,aio 
  39.         (这些模块位于src/event/modules目录中)等等,所以Nginx对事件模块进行 
  40.         了一层抽象,方便在不同的系统上使用不同的事件模型,也便于扩展新的事件 
  41.         模型。从此过后,将把注意力主要集中在epoll上。 
  42.          
  43.         此处的init回调,其实就是调用了ngx_epoll_init函数。module->actions结构 
  44.         封装了epoll的所有接口函数。Nginx就是通过actions结构将epoll注册到事件 
  45.         抽象层中。actions的类型是ngx_event_actions_t,位于src/event/ngx_event.h 
  46.         */  
  47.         if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {  
  48.             /* fatal */  
  49.             exit(2);  
  50.         }  
  51.         break;  /*跳出循环,只可能使用一个具体的事件模型*/  
  52.     }  
  53.     。。。。。。。。。。。。。。。。。。。。。。。。。  
  54.     /*创建一个connection数组,维护所有的connection; 
  55.     本过程已经是在worker进程中了,所以是每个worker都有自己的 
  56.     connection数组。 
  57.     */  
  58.     cycle->connections =  
  59.         ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);  
  60.     if (cycle->connections == NULL) {  
  61.         return NGX_ERROR;  
  62.     }  
  63.     c = cycle->connections;  
  64.     /*创建一个读事件数组*/  
  65.     cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,  
  66.                                    cycle->log);  
  67.     if (cycle->read_events == NULL) {  
  68.         return NGX_ERROR;  
  69.     }  
  70.     rev = cycle->read_events;  
  71.     for (i = 0; i < cycle->connection_n; i++) {  
  72.         rev[i].closed = 1;  
  73.         rev[i].instance = 1;  
  74.     }  
  75.       
  76.     /*创建一个写事件数组*/  
  77.     cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,  
  78.                                     cycle->log);  
  79.     if (cycle->write_events == NULL) {  
  80.         return NGX_ERROR;  
  81.     }  
  82.     wev = cycle->write_events;  
  83.     for (i = 0; i < cycle->connection_n; i++) {  
  84.         wev[i].closed = 1;  
  85.     }  
  86.     i = cycle->connection_n;  
  87.     next = NULL;  
  88.       
  89.     /*初始化整个connection数组,connection数组使用得很是巧妙, 
  90.     能够快速的获取释放一个连接结构。下一篇画个图来详细看看 
  91.     这个connection。 
  92.     */  
  93.     do {  
  94.         i--;  
  95.         c[i].data = next;  
  96.         c[i].read = &cycle->read_events[i];  
  97.         c[i].write = &cycle->write_events[i];  
  98.         c[i].fd = (ngx_socket_t) -1;  
  99.         next = &c[i];  
  100.     } while (i);  
  101.     cycle->free_connections = next;  
  102.     cycle->free_connection_n = cycle->connection_n;  
  103.     /* for each listening socket */  
  104.       
  105.     /*为每个监听套接字从connection数组中分配一个连接,即一个slot*/  
  106.     ls = cycle->listening.elts;  
  107.     for (i = 0; i < cycle->listening.nelts; i++) {  
  108.         /*从connection中取得一个新的连接slot*/  
  109.         c = ngx_get_connection(ls[i].fd, cycle->log);  
  110.         if (c == NULL) {  
  111.             return NGX_ERROR;  
  112.         }  
  113.         c->log = &ls[i].log;  
  114.         c->listening = &ls[i];  
  115.         ls[i].connection = c;  
  116.         rev = c->read;  
  117.         rev->log = c->log;  
  118.         rev->accept = 1; /*读事件发生,调用accept*/  
  119.     。。。。。。。。。。。。。。。。。。。。  
  120. #if (NGX_WIN32)   
  121.         。。。。。。。。。。。。。。。。。。。  
  122. #else   
  123.         /*注册监听套接口读事件的回调函数ngx_event_accept*/  
  124.         rev->handler = ngx_event_accept;  
  125.         /*使用了accept_mutex,暂时不将监听套接字放入epoll中 
  126.         而是等到worker抢到accept互斥体后,再放入epoll,避免 
  127.         惊群的发生。 
  128.         */  
  129.         if (ngx_use_accept_mutex) {  
  130.             continue;  
  131.         }  
  132.         if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {  
  133.             if (ngx_add_conn(c) == NGX_ERROR) {  
  134.                 return NGX_ERROR;  
  135.             }  
  136.         } else {  
  137.             /*没有使用accept互斥体,那么就在此处将监听套接字放入 
  138.             epoll中。 
  139.             */  
  140.             if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {  
  141.                 return NGX_ERROR;  
  142.             }  
  143.         }  
  144. #endif   
  145.     }  
  146.     return NGX_OK;  
  147. }  

 

至此,事件驱动的初始化三步曲就分析完了。

 

本文摘自:http://blog.csdn.net/marcky/article/details/6025647感谢作者

阅读(1932) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~