当前位置:Gxlcms > PHP教程 > nginx源码学习笔记(二十)——event模块一——初始化

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

时间:2021-07-01 10:21:17 帮助过:35人阅读

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

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

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

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

1.创建conf(creat_conf):

ngx_event_create_conf()

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

2.读取配置文件:

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

[cpp] view plaincopyprint?

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


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

[cpp] view plaincopyprint?

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


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

[cpp] view plaincopyprint?

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


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回调函数。这里本人也在纠结为什么,希望在学完全部代码后,能够找到答案。

[cpp] view plaincopyprint?

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


4.ngx_event_process_init

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

[cpp] view plaincopyprint?

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


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

以上就介绍了nginx 源码学习笔记(二十)—— event 模块一 ——初始化,包括了IOC,计数器方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

人气教程排行