nginx 源码学习笔记(二十)—— event 模块(一) ——初始化

读完之前的学习笔记,相信已经对nginx的启动流程有了一定的认识,从这一节起我们想深入各个模块,学习各个模块的内的主要操作。

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


今天我们就来学习下event模块,在之前的启动里多次提到了调用各个模块的钩子函数,我们先来回忆一下关于event模块钩子函数的执行,也是event模块启动的步骤:


1.创建conf(creat_conf):

ngx_event_create_conf()

该方法,主要是创建了一个ngx_event_conf_t结构体,并且分配内存空间。


2.读取配置文件:

例如读取到的文件有如下行:

  1. events   
  2. {  
  3.   use epoll;  
  4.   worker_connections 10240;  
  5. }  


这个地方的events是一个block指令,在大括号内可以配置很多指令,这些指令定义在src/event/ngx_event.c中

  1. static ngx_command_t  ngx_event_core_commands[] = {  
  2.   
  3.     { ngx_string("worker_connections"),  
  4.       NGX_EVENT_CONF|NGX_CONF_TAKE1,  
  5.       ngx_event_connections,  
  6.       0,  
  7.       0,  
  8.       NULL },  
  9.                 ...(此处省略)  
  10.   
  11.     { ngx_string("connections"),  
  12.       NGX_EVENT_CONF|NGX_CONF_TAKE1,  
  13.       ngx_event_connections,  
  14.       0,  
  15.       0,  
  16.       NULL },  
  17.   
  18.     { ngx_string("use"),  
  19.       NGX_EVENT_CONF|NGX_CONF_TAKE1,  
  20.       ngx_event_use,  
  21.       0,  
  22.       0,  
  23.       NULL },  
  24.       ngx_null_command  
  25. };  


当解析到events是会回调如下函数:

  1. src/event/ngx_event.c  
  2. static char *  
  3. ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  
  4. {  
  5.     char                 *rv;  
  6.     void               ***ctx;  
  7.     ngx_uint_t            i;  
  8.     ngx_conf_t            pcf;  
  9.     ngx_event_module_t   *m;  
  10.   
  11.     /* count the number of the event modules and set up their indices */  
  12.     //计算event模块数量,并且记录  
  13.     ngx_event_max_module = 0;  
  14.     for (i = 0; ngx_modules[i]; i++) {  
  15.         if (ngx_modules[i]->type != NGX_EVENT_MODULE) {  
  16.             continue;  
  17.         }  
  18.   
  19.         ngx_modules[i]->ctx_index = ngx_event_max_module++;  
  20.     }  
  21.   
  22.     ctx = ngx_pcalloc(cf->pool, sizeof(void *));  
  23.     if (ctx == NULL) {  
  24.         return NGX_CONF_ERROR;  
  25.     }  
  26.     //为每一个event模块分配空间,用来保存响应配置结构的地址  
  27.     //共分配了ngx_event_max_module个空间  
  28.     *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));  
  29.     if (*ctx == NULL) {  
  30.         return NGX_CONF_ERROR;  
  31.     }  
  32.   
  33.     *(void **) conf = ctx;  
  34.   
  35.     for (i = 0; ngx_modules[i]; i++) {  
  36.         if (ngx_modules[i]->type != NGX_EVENT_MODULE) {  
  37.             continue;  
  38.         }  
  39.   
  40.         m = ngx_modules[i]->ctx;  
  41.         //循环调用每个模块的creat_conf钩子函数,用于创建配置结构  
  42.         if (m->create_conf) {  
  43.             (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);  
  44.             if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {  
  45.                 return NGX_CONF_ERROR;  
  46.             }  
  47.         }  
  48.     }  
  49.   
  50.     pcf = *cf;  
  51.     cf->ctx = ctx;  
  52.     cf->module_type = NGX_EVENT_MODULE;  
  53.     cf->cmd_type = NGX_EVENT_CONF;  
  54.     //由于events是一个block指令,events域下还可以配置很多其他指令,  
  55.     //比如之前提过的use等,现在开始解析events block中的指令,完成初始化工作。  
  56.     rv = ngx_conf_parse(cf, NULL);  
  57.   
  58.     *cf = pcf;  
  59.   
  60.     if (rv != NGX_CONF_OK)  
  61.         return rv;  
  62.       
  63.     for (i = 0; ngx_modules[i]; i++) {  
  64.         if (ngx_modules[i]->type != NGX_EVENT_MODULE) {  
  65.             continue;  
  66.         }  
  67.         m = ngx_modules[i]->ctx;  
  68.         //循环执行每个event模块的init_conf函数,初始化配置结构  
  69.         if (m->init_conf) {  
  70.             rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);  
  71.             if (rv != NGX_CONF_OK) {  
  72.                 return rv;  
  73.             }  
  74.         }  
  75.     }  
  76.   
  77.     return NGX_CONF_OK;  
  78. }  


ngx_events_block()函数中最重要的一个过程就是调用ngx_conf_parse(cf, NULL),此处调用ngx_conf_parse()的作用就是完成配置文件中events{}这个block的解析,从而调用其下所有的配置指令的回调函数,完成解析配置文件的初始化工作。但是这里我个人有个问题,待问完前辈之后,在指明问题和答案******。


2.初始化conf(init_conf)

ngx_event_init_conf()

该方法,主要是初始化ngx_event_conf_t结构体。


3.ngx_event_module_init

从名字上看是模块的初始化操作,但是纵观各个模块源代码,发现很多模块都没有init回调函数。这里本人也在纠结为什么,希望在学完全部代码后,能够找到答案。

  1. src/event/ngx_event.c  
  2. static ngx_int_t  
  3. ngx_event_module_init(ngx_cycle_t *cycle)  
  4. {  
  5.     void              ***cf;  
  6.     u_char              *shared;  
  7.     size_t               size, cl;  
  8.     ngx_shm_t            shm;  
  9.     ngx_time_t          *tp;  
  10.     ngx_core_conf_t     *ccf;  
  11.     ngx_event_conf_t    *ecf;  
  12.       
  13.     //判断ngx_events_module是否调用过初始化conf操作  
  14.     cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);  
  15.   
  16.     if (cf == NULL) {  
  17.         ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,  
  18.                       "no \"events\" section in configuration");  
  19.         return NGX_ERROR;  
  20.     }  
  21.       
  22.     //获取ngx_event_core_module模块的配置结构  
  23.     ecf = (*cf)[ngx_event_core_module.ctx_index];  
  24.       
  25.     //查看是否是event中的模块,例如use 。。。。  
  26.     if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {  
  27.         ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,  
  28.                       "using the \"%s\" event method", ecf->name);  
  29.     }  
  30.     //获取ngx_core_module模块的配置结构  
  31.     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  
  32.       
  33.     //从ngx_core_module模块的配置结构中获取timer_resolution参数  
  34.     ngx_timer_resolution = ccf->timer_resolution;  
  35.   
  36. #if !(NGX_WIN32)  
  37.     {  
  38.     ngx_int_t      limit;  
  39.     struct rlimit  rlmt;  
  40.       
  41.     //获取当前进程能够打开的最大文件数     man getrlimit  
  42.     if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {  
  43.         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  44.                       "getrlimit(RLIMIT_NOFILE) failed, ignored");  
  45.   
  46.     } else {  
  47.         //如果ngx_event_core_module模块连接数大于当前(软)限制  
  48.         //并且ngx_core_module最大连接数无限制  
  49.         //或者ngx_event_core_module连接数大于ngx_core_module最大连接数  
  50.         if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur  
  51.             && (ccf->rlimit_nofile == NGX_CONF_UNSET  
  52.                 || ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))  
  53.         {  
  54.             limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?  
  55.                          (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;  
  56.   
  57.             ngx_log_error(NGX_LOG_WARN, cycle->log, 0,  
  58.                           "%ui worker_connections are more than "  
  59.                           "open file resource limit: %i",  
  60.                           ecf->connections, limit);  
  61.         }  
  62.     }  
  63.     }  
  64. #endif /* !(NGX_WIN32) */  
  65.   
  66.     //如果关闭了master进程,就返回  
  67.     //因为关闭了master进程就是单进程工作方式,  
  68.     //之后的操作时创建共享内存实现锁等工作,单进程不需要。  
  69.     if (ccf->master == 0) {  
  70.         return NGX_OK;  
  71.     }  
  72.       
  73.     //如果已经存在accept互斥体了,不需要再重复创建了  
  74.     if (ngx_accept_mutex_ptr) {  
  75.         return NGX_OK;  
  76.     }  
  77.   
  78.   
  79.     /* cl should be equal or bigger than cache line size */  
  80.   
  81.     cl = 128;  
  82.     //这里创建size大小的共享内存,这块共享内存将被均分成三段  
  83.     size = cl            /* ngx_accept_mutex */  
  84.            + cl          /* ngx_connection_counter */  
  85.            + cl;         /* ngx_temp_number */  
  86.   
  87.     //准备共享内存,大小为size,命名nginx_shared_zone,  
  88.     shm.size = size;  
  89.     shm.name.len = sizeof("nginx_shared_zone");  
  90.     shm.name.data = (u_char *) "nginx_shared_zone";  
  91.     shm.log = cycle->log;  
  92.       
  93.     //创建共享内存,起始地址保存在shm.addr  
  94.     if (ngx_shm_alloc(&shm) != NGX_OK) {  
  95.         return NGX_ERROR;  
  96.     }  
  97.     //获取起始地址保存  
  98.     shared = shm.addr;  
  99.   
  100.     //accept互斥体取得共享内存的第一段cl大小内存  
  101.     ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;  
  102.     ngx_accept_mutex.spin = (ngx_uint_t) -1;  
  103.     /*创建accept互斥体 
  104.      
  105.     accept互斥体的实现依赖是否支持原子操作,如果有相应的原子操作; 
  106.     就是用取得的这段共享内存来实现accept互斥体;否则,将使用文件锁 
  107.     来实现accept互斥体。 
  108.      
  109.     accept互斥体的作用是:避免惊群和实现worker进程的负载均衡。 
  110.      
  111.     */  
  112.     if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data)  
  113.         != NGX_OK)  
  114.     {  
  115.         return NGX_ERROR;  
  116.     }  
  117.       
  118.     //获取内存的第二段cl大小的地址  
  119.     ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);  
  120.   
  121.     (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);  
  122.   
  123.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,  
  124.                    "counter: %p, %d",  
  125.                    ngx_connection_counter, *ngx_connection_counter);  
  126.     //获取内存的第三段cl大小的地址  
  127.     ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);  
  128.   
  129.     tp = ngx_timeofday();  
  130.   
  131.     ngx_random_number = (tp->msec << 16) + ngx_pid;  
  132.   
  133.     return NGX_OK;  
  134. }  


4.ngx_event_process_init

在之前的worker进程分析中有提到过,当创建了一个worker进程后,worker进程首先就会做进程的初始化工作,此时会调用ngx_event_process_init函数。

  1. src/event/ngx_event.c  
  2. static ngx_int_t  
  3. ngx_event_process_init(ngx_cycle_t *cycle)  
  4. {  
  5.     ngx_uint_t           m, i;  
  6.     ngx_event_t         *rev, *wev;  
  7.     ngx_listening_t     *ls;  
  8.     ngx_connection_t    *c, *next, *old;  
  9.     ngx_core_conf_t     *ccf;  
  10.     ngx_event_conf_t    *ecf;  
  11.     ngx_event_module_t  *module;  
  12.       
  13.     //和之前一样,获取响应模块的配置结构  
  14.     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  
  15.     ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);  
  16.       
  17.     //master进程打开,worker进程大于1,已经创建了accept_mutex  
  18.     //才打开accept互斥体  
  19.     if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {  
  20.         ngx_use_accept_mutex = 1; //使用互斥体  
  21.         ngx_accept_mutex_held = 0; //是否获得accept互斥体  
  22.         ngx_accept_mutex_delay = ecf->accept_mutex_delay;//争抢互斥体失败后,等待下次争抢时间间隔  
  23.   
  24.     } else {  
  25.         ngx_use_accept_mutex = 0;  
  26.     }  
  27.   
  28. #if (NGX_THREADS)  
  29.     //线程先不讲  
  30. #endif  
  31.     //初始化计数器,此处将会创建一颗红黑树,来维护计时器,之后会详细讲解  
  32.     if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {  
  33.         return NGX_ERROR;  
  34.     }  
  35.   
  36.     for (m = 0; ngx_modules[m]; m++) {  
  37.         //这里之前讲过,跳过非NGX_EVENT_MODULE模块  
  38.         if (ngx_modules[m]->type != NGX_EVENT_MODULE) {  
  39.             continue;  
  40.         }  
  41.         //非use配置指令指定的模块跳过,linux默认epoll  
  42.         if (ngx_modules[m]->ctx_index != ecf->use) {  
  43.             continue;  
  44.         }  
  45.   
  46.         module = ngx_modules[m]->ctx;  
  47.         /*调用具体时间模块的init函数 
  48.          
  49.         由于nginx实现了很多事件模块,比如:epoll、poll、select、dqueue、aio 
  50.         (这些模块位于src/event/modules目录中),所以nginx对时间模块进行了一层抽象, 
  51.         方便了不同的系统使用不同的事件模型,也便于扩展新的时间模型,我们的重点应该 
  52.         放在epoll上。 
  53.          
  54.         此处的init回调,其实就是调用了ngx_epoll_init函数。module->actions结构封装了 
  55.         epoll的所有接口函数。nginx就是通过actions结构将epoll注册到事件抽象层中。 
  56.         actions的类型是ngx_event_action_t,位于src/event/ngx_event.h 
  57.          
  58.         这些具体的内容会在下一节中重点讲解。 
  59.          
  60.         */  
  61.         if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {  
  62.             /* fatal */  
  63.             exit(2);  
  64.         }  
  65.   
  66.         break;  
  67.     }  
  68. //此处省略部分内容  
  69.     //创建全局的ngx_connection_t数组,保存所有的connection  
  70.     //由于这个过程是在各个worker进程中执行的,所以每个worker都有自己的connection数组  
  71.     cycle->connections =  
  72.         ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);  
  73.     if (cycle->connections == NULL) {  
  74.         return NGX_ERROR;  
  75.     }  
  76.   
  77.     c = cycle->connections;  
  78.       
  79.     //创建一个读事件数组  
  80.     cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,  
  81.                                    cycle->log);  
  82.     if (cycle->read_events == NULL) {  
  83.         return NGX_ERROR;  
  84.     }  
  85.   
  86.     rev = cycle->read_events;  
  87.     for (i = 0; i < cycle->connection_n; i++) {  
  88.         rev[i].closed = 1;  
  89.         rev[i].instance = 1;  
  90. #if (NGX_THREADS)  
  91.         rev[i].lock = &c[i].lock;  
  92.         rev[i].own_lock = &c[i].lock;  
  93. #endif  
  94.     }  
  95.     //创建一个写事件数组  
  96.     cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,  
  97.                                     cycle->log);  
  98.     if (cycle->write_events == NULL) {  
  99.         return NGX_ERROR;  
  100.     }  
  101.   
  102.     wev = cycle->write_events;  
  103.     for (i = 0; i < cycle->connection_n; i++) {  
  104.         wev[i].closed = 1;  
  105. #if (NGX_THREADS)  
  106.         wev[i].lock = &c[i].lock;  
  107.         wev[i].own_lock = &c[i].lock;  
  108. #endif  
  109.     }  
  110.   
  111.     i = cycle->connection_n;  
  112.     next = NULL;  
  113.     //初始化整个connection数组  
  114.     do {  
  115.         i--;  
  116.   
  117.         c[i].data = next;  
  118.         c[i].read = &cycle->read_events[i];  
  119.         c[i].write = &cycle->write_events[i];  
  120.         c[i].fd = (ngx_socket_t) -1;  
  121.   
  122.         next = &c[i];  
  123.   
  124. #if (NGX_THREADS)  
  125.         c[i].lock = 0;  
  126. #endif  
  127.     } while (i);  
  128.   
  129.     cycle->free_connections = next;  
  130.     cycle->free_connection_n = cycle->connection_n;  
  131.   
  132.     /* for each listening socket */  
  133.     //为每个监听套接字从connection数组中分配一个连接,即一个slot  
  134.     ls = cycle->listening.elts;  
  135.     for (i = 0; i < cycle->listening.nelts; i++) {  
  136.         //从conneciton中取得一个新的连接solt  
  137.         c = ngx_get_connection(ls[i].fd, cycle->log);  
  138.   
  139.         if (c == NULL) {  
  140.             return NGX_ERROR;  
  141.         }  
  142.   
  143.         c->log = &ls[i].log;  
  144.   
  145.         c->listening = &ls[i];  
  146.         ls[i].connection = c;  
  147.   
  148.         rev = c->read;  
  149.   
  150.         rev->log = c->log;  
  151.         rev->accept = 1; //读时间发生,调用accept  
  152.   
  153. #if (NGX_HAVE_DEFERRED_ACCEPT)  
  154.         //省略  
  155. #endif  
  156.   
  157.         if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {  
  158.             if (ls[i].previous) {  
  159.   
  160.                 /* 
  161.                  * delete the old accept events that were bound to 
  162.                  * the old cycle read events array 
  163.                  */  
  164.   
  165.                 old = ls[i].previous->connection;  
  166.   
  167.                 if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)  
  168.                     == NGX_ERROR)  
  169.                 {  
  170.                     return NGX_ERROR;  
  171.                 }  
  172.   
  173.                 old->fd = (ngx_socket_t) -1;  
  174.             }  
  175.         }  
  176.   
  177.         //注册监听套接口毒事件的回调函数 ngx_event_accept  
  178.         rev->handler = ngx_event_accept;  
  179.           
  180.         //使用了accept_mutex,暂时不将监听套接字放入epoll中,而是  
  181.         //等到worker抢到accept互斥体后,再放入epoll,避免惊群的发生  
  182.         if (ngx_use_accept_mutex) {  
  183.             continue;  
  184.         }  
  185.           
  186.           
  187.         if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {  
  188.             if (ngx_add_conn(c) == NGX_ERROR) {  
  189.                 return NGX_ERROR;  
  190.             }  
  191.   
  192.         } else {  
  193.             //没有使用accept互斥体,那么就将此监听套接字放入epoll中。  
  194.             if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {  
  195.                 return NGX_ERROR;  
  196.             }  
  197.         }  
  198.   
  199. #endif  
  200.   
  201.     }  
  202.   
  203.     return NGX_OK;  
  204. }  


到现在为止,事件驱动的初始化已经完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值