nginx epoll详解

nginx epoll 事件模型

 

    nginx做为一个异步高效的事件驱动型web服务器,在linux平台中当系统支持epoll时nginx默认采用epoll来高效的处理事件。nginx中使用ngx_event_t结构来表示一个事件,先介绍下ngx_event_t结构体中成员的含义:

 

 
  1. struct ngx_event_s {

  2. void *data; //与事件关联的对象,常指向事件所在的ngx_connection_t连接对象

  3.  
  4. unsigned write:1; //可写标识位,1表示对应的tcp连接是可写的

  5.  
  6. unsigned accept:1;// 1表示对应的连接是处于监听状态的连接,即可接收新的连接

  7.  
  8. /* used to detect the stale events in kqueue, rtsig, and epoll */

  9. unsigned instance:1; //可来区分事件是否已过期

  10.  
  11. /*

  12. * the event was passed or would be passed to a kernel;

  13. * in aio mode - operation was posted.

  14. */

  15. unsigned active:1;// 1表示事件活跃,即事件已添加到epoll中

  16.  
  17. unsigned disabled:1;//epoll中不使用该标识位

  18.  
  19. /* the ready event; in aio mode 0 means that no operation can be posted */

  20. unsigned ready:1; //事件已就绪(即可读或可写)

  21.  
  22. unsigned oneshot:1;//epoll不使用该标识位

  23.  
  24. /* aio operation is complete */

  25. unsigned complete:1;//aio中使用,表示 事件对应的aio异步操作已完成(io_getevents函数已成功返回)

  26.  
  27. unsigned eof:1;// 1表示当前处理的字符流已完成,如调用recv读取连接数据时返回0,此时置该标识位为1

  28. unsigned error:1;// 1表示事件处理过程中发生错误

  29.  
  30. unsigned timedout:1; //事件是否超时,1:表示超时。超时后事件对应的请求不需再被处理(对于http模块来说事件超时后直接关闭请求)

  31. unsigned timer_set:1; //为1时表示这个事件在定时器中

  32.  
  33. unsigned delayed:1;// 1表示 需延迟处理该事件,常用于限速功能中

  34.  
  35. unsigned deferred_accept:1;//延迟接收接连,即当连接中收到对象发送的数据后才真正建立连接

  36.  
  37. /* the pending eof reported by kqueue, epoll or in aio chain operation */

  38. unsigned pending_eof:1;// 1表示TCP连接对向关闭读端,即epoll返回EPOLLRDHUP

  39.  
  40. #if !(NGX_THREADS)

  41. unsigned posted_ready:1;//该标识位在1.5.5版本源码中只在ngx_epoll_process_events函数中有置位,其它地方并没有用到

  42. #endif

  43.  
  44. #if (NGX_WIN32)

  45. /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */

  46. unsigned accept_context_updated:1;

  47. #endif

  48.  
  49. #if (NGX_HAVE_KQUEUE)

  50. unsigned kq_vnode:1;

  51.  
  52. /* the pending errno reported by kqueue */

  53. int kq_errno;

  54. #endif

  55.  
  56. /*

  57. * kqueue only:

  58. * accept: number of sockets that wait to be accepted

  59. * read: bytes to read when event is ready

  60. * or lowat when event is set with NGX_LOWAT_EVENT flag

  61. * write: available space in buffer when event is ready

  62. * or lowat when event is set with NGX_LOWAT_EVENT flag

  63. *

  64. * iocp: TODO

  65. *

  66. * otherwise:

  67. * accept: 1 if accept many, 0 otherwise

  68. */

  69.  
  70. #if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)

  71. int available;

  72. #else

  73. unsigned available:1;// 1表示每次调用accept时尽可能多的接收TCP连接,与multi_accept配置项对应

  74. #endif

  75.  
  76. ngx_event_handler_pt handler; // 事件产生后的回调函数句柄

  77.  
  78.  
  79. #if (NGX_HAVE_AIO)

  80.  
  81. #if (NGX_HAVE_IOCP)

  82. ngx_event_ovlp_t ovlp;

  83. #else

  84. struct aiocb aiocb;

  85. #endif

  86.  
  87. #endif

  88.  
  89. ngx_uint_t index; //epoll中不使用

  90.  
  91. ngx_log_t *log; //ngx_log_t对象

  92.  
  93. ngx_rbtree_node_t timer;

  94.  
  95. unsigned closed:1; // 1表示事件已关闭

  96.  
  97. /* to test on worker exit */

  98. unsigned channel:1;// 只在ngx_add_channel_event函数中有置位,其它地方没用到

  99. unsigned resolver:1; // resolver功能中使用?

  100.  
  101. #if (NGX_THREADS)

  102.  
  103. unsigned locked:1;

  104.  
  105. unsigned posted_ready:1;

  106. unsigned posted_timedout:1;

  107. unsigned posted_eof:1;

  108.  
  109. #if (NGX_HAVE_KQUEUE)

  110. /* the pending errno reported by kqueue */

  111. int posted_errno;

  112. #endif

  113.  
  114. #if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)

  115. int posted_available;

  116. #else

  117. unsigned posted_available:1;

  118. #endif

  119.  
  120. ngx_atomic_t *lock;

  121. ngx_atomic_t *own_lock;

  122.  
  123. #endif

  124.  
  125. /* the links of the posted queue */

  126. ngx_event_t *next;

  127. ngx_event_t **prev;

  128.  
  129.  
  130. #if 0

  131.  
  132. /* the threads support */

  133.  
  134. /*

  135. * the event thread context, we store it here

  136. * if $(CC) does not understand __thread declaration

  137. * and pthread_getspecific() is too costly

  138. */

  139.  
  140. void *thr_ctx;

  141.  
  142. #if (NGX_EVENT_T_PADDING)

  143.  
  144. /* event should not cross cache line in SMP */

  145.  
  146. uint32_t padding[NGX_EVENT_T_PADDING];

  147. #endif

  148. #endif

  149. };

  150.  
  151.  
  152. #if (NGX_HAVE_FILE_AIO)

  153.  
  154. struct ngx_event_aio_s {

  155. void *data;

  156. ngx_event_handler_pt handler;

  157. ngx_file_t *file;

  158.  
  159. ngx_fd_t fd;

  160.  
  161. #if (NGX_HAVE_EVENTFD)

  162. int64_t res;

  163. #if (NGX_TEST_BUILD_EPOLL)

  164. ngx_err_t err;

  165. size_t nbytes;

  166. #endif

  167. #else

  168. ngx_err_t err;

  169. size_t nbytes;

  170. #endif

  171.  
  172. #if (NGX_HAVE_AIO_SENDFILE)

  173. off_t last_offset;

  174. #endif

  175.  
  176. ngx_aiocb_t aiocb;

  177. ngx_event_t event;

  178. };

  179.  
  180. #endif

 

 

    nginx中使用ngx_epoll_module模块来封装epoll机制处理事件,ngx_epoll_module模块只对两个配置项感兴趣,其ngx_command_t结构如下:

 

 
  1. static ngx_command_t ngx_epoll_commands[] = {

  2.  
  3. {

  4. /***epoll_events配置项表示epoll_wait函数每次最多返回多少个事件,在ngx_epoll_init函数中

  5. 会预先分配epoll_events配置项指定的epoll_event结构个数**/

  6. ngx_string("epoll_events"),

  7. NGX_EVENT_CONF|NGX_CONF_TAKE1,

  8. ngx_conf_set_num_slot,

  9. 0,

  10. offsetof(ngx_epoll_conf_t, events),

  11. NULL },

  12.  
  13. {

  14. /***worker_aio_requests配置项表示创建的aio context能并发处理异步事件的个数,即io_setup函数的第一个参数***/

  15. ngx_string("worker_aio_requests"),

  16. NGX_EVENT_CONF|NGX_CONF_TAKE1,

  17. ngx_conf_set_num_slot,

  18. 0,

  19. offsetof(ngx_epoll_conf_t, aio_requests),

  20. NULL },

  21.  
  22. ngx_null_command

  23. };

 

    ngx_epoll_module的ngx_event_module_t结构如下:

 

 
  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. {

  7. //向epoll中添加事件时调用

  8. ngx_epoll_add_event, /* add an event */

  9. //从epoll中删除事件时调用

  10. ngx_epoll_del_event, /* delete an event */

  11. /***epoll中不存在enable/disable事件的情况,这里默认设置成添加/删除事件的函数***/

  12. ngx_epoll_add_event, /* enable an event */

  13. ngx_epoll_del_event, /* disable an event */

  14. //向epoll中添加tcp连接时调用,每个tcp连接对象一个读事件和一个写事件

  15. ngx_epoll_add_connection, /* add an connection */

  16. //从epoll中删除事件时调用

  17. ngx_epoll_del_connection, /* delete an connection */

  18. NULL, /* process the changes */

  19. // epoll 事件处理函数

  20. ngx_epoll_process_events, /* process the events */

  21. //epoll模块初始化函数

  22. ngx_epoll_init, /* init the events */

  23. //epoll模块清理函数只在多线程模型中被调用

  24. ngx_epoll_done, /* done the events */

  25. }

  26. };

 

    ngx_epoll_create_conf在配置项解析前调用用来初始化配置结构,ngx_epoll_init_conf函数在配置项解析完后调用,如果配置文件是不存在epoll_events或worker_aio_requests配置项,默认将epoll_events设置为512,worker_aio_requests设置为32。ngx_epoll_module_ctx结构体中后十个函数对应于ngx_event_actions_t结构,它是事件模块独有的结构。ngx_epoll_init函数在什么时候被调用呢,它在nginx启动过程中每个worker进程启动后被调用(由ngx_event_core_module的ngx_event_process_init函数调用)。

 

ngx_epoll_module源码分析

ngx_epoll_init函数:

 

 
  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.  
  6. // 获取ngx_epoll_module模块存放配置项的结构

  7. epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);

  8.  
  9. if (ep == -1) {

  10. // 创建epoll,成功返回描述符,失败返回-1

  11. ep = epoll_create(cycle->connection_n / 2);

  12.  
  13. if (ep == -1) {

  14. ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,

  15. "epoll_create() failed");

  16. return NGX_ERROR;

  17. }

  18.  
  19. /***如果系统支持aio , 这里初始化aio***/

  20. #if (NGX_HAVE_FILE_AIO)

  21. ngx_epoll_aio_init(cycle, epcf);

  22.  
  23. #endif

  24. }

  25.  
  26. /***预分配events个epoll_event结构, epcf->events由epoll_events配置项指定,默认为512***/

  27. if (nevents < epcf->events) {

  28. if (event_list) {

  29. ngx_free(event_list);

  30. }

  31.  
  32. event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,

  33. cycle->log);

  34. if (event_list == NULL) {

  35. return NGX_ERROR;

  36. }

  37. }

  38.  
  39. nevents = epcf->events;

  40.  
  41. //指定I/O读写的方法

  42. ngx_io = ngx_os_io;

  43.  
  44. // 设置ngx_event_actions接口,后续通过ngx_event_actions来调用epoll模块中的方法

  45. ngx_event_actions = ngx_epoll_module_ctx.actions;

  46.  
  47. /***nginx使用epoll事件模型时NGX_HAVE_CLEAR_EVENT宏被定义, NGX_USE_CLEAR_EVENT宏表示使用epoll的ET模式***/

  48. #if (NGX_HAVE_CLEAR_EVENT)

  49. ngx_event_flags = NGX_USE_CLEAR_EVENT

  50. #else

  51. ngx_event_flags = NGX_USE_LEVEL_EVENT

  52. #endif

  53. |NGX_USE_GREEDY_EVENT

  54. |NGX_USE_EPOLL_EVENT;

  55.  
  56. return NGX_OK;

  57. }

 

ngx_epoll_add_event函数:

 
  1. static ngx_int_t

  2. ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

  3. {

  4. int op;

  5. uint32_t events, prev;

  6. ngx_event_t *e;

  7. ngx_connection_t *c;

  8. struct epoll_event ee;

  9.  
  10. //获取事件关联的连接

  11. c = ev->data;

  12.  
  13. events = (uint32_t) event;

  14.  
  15. /***根据event参数判断当前是添加读事件还是写事件***/

  16. if (event == NGX_READ_EVENT) {

  17. e = c->write;

  18. prev = EPOLLOUT;

  19. #if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)

  20. events = EPOLLIN|EPOLLRDHUP;

  21. #endif

  22.  
  23. } else {

  24. e = c->read;

  25. prev = EPOLLIN|EPOLLRDHUP;

  26. #if (NGX_WRITE_EVENT != EPOLLOUT)

  27. events = EPOLLOUT;

  28. #endif

  29. }

  30.  
  31. /***如果当前需添加读事件,就通过active标识判断读事件所关联的连接对应的写事件是否活跃(

  32. 活跃表示事件已添加到epoll中)。***/

  33. if (e->active) {

  34. op = EPOLL_CTL_MOD;

  35. events |= prev;

  36.  
  37. } else {

  38. op = EPOLL_CTL_ADD;

  39. }

  40.  
  41. //将flags参数加入到epoll标志中

  42. ee.events = events | (uint32_t) flags;

  43. /*** ptr存储事件关联的连接对象(ngx_connection_t*)及事件过期比特位,

  44. linux平台中任何对象的地址最低位必定为零***/

  45. ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);

  46.  
  47. ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,

  48. "epoll add event: fd:%d op:%d ev:%08XD",

  49. c->fd, op, ee.events);

  50.  
  51. //向epoll中添加事件

  52. if (epoll_ctl(ep, op, c->fd, &ee) == -1) {

  53. ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

  54. "epoll_ctl(%d, %d) failed", op, c->fd);

  55. return NGX_ERROR;

  56. }

  57.  
  58. //标识事件活跃

  59. ev->active = 1;

  60. #if 0

  61. ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;

  62. #endif

  63.  
  64. return NGX_OK;

  65. }

 

ngx_epoll_del_event函数:

 
  1. static ngx_int_t

  2. ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)

  3. {

  4. int op;

  5. uint32_t prev;

  6. ngx_event_t *e;

  7. ngx_connection_t *c;

  8. struct epoll_event ee;

  9.  
  10. /*

  11. * when the file descriptor is closed, the epoll automatically deletes

  12. * it from its queue, so we do not need to delete explicitly the event

  13. * before the closing the file descriptor

  14. */

  15.  
  16. /***上面的注释说得很清楚了,当文件描述符被关闭后,epoll会自动将其删除。***/

  17. if (flags & NGX_CLOSE_EVENT) {

  18. ev->active = 0;

  19. return NGX_OK;

  20. }

  21.  
  22. //获取事件关联的连接

  23. c = ev->data;

  24.  
  25. /***根据event参数判断当前是删除读事件还是写事件***/

  26. if (event == NGX_READ_EVENT) {

  27. e = c->write;

  28. prev = EPOLLOUT;

  29.  
  30. } else {

  31. e = c->read;

  32. prev = EPOLLIN|EPOLLRDHUP;

  33. }

  34.  
  35. /***参考ngx_epoll_add_event函数***/

  36. if (e->active) {

  37. op = EPOLL_CTL_MOD;

  38. ee.events = prev | (uint32_t) flags;

  39. ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);

  40.  
  41. } else {

  42. op = EPOLL_CTL_DEL;

  43. ee.events = 0;

  44. ee.data.ptr = NULL;

  45. }

  46.  
  47. ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,

  48. "epoll del event: fd:%d op:%d ev:%08XD",

  49. c->fd, op, ee.events);

  50.  
  51. //从epoll中删除事件

  52. if (epoll_ctl(ep, op, c->fd, &ee) == -1) {

  53. ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,

  54. "epoll_ctl(%d, %d) failed", op, c->fd);

  55. return NGX_ERROR;

  56. }

  57.  
  58. //清除事件活跃标识

  59. ev->active = 0;

  60.  
  61. return NGX_OK;

  62. }

 

ngx_epoll_add_connection及ngx_epoll_del_connection函数

    这两个函数的实现很简单,也是通过调用epoll_ctl添加事件,只是会同时将读/写事件一起添加进epoll,这里不再列出源码。

 

ngx_epoll_process_events函数:

 
  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_event_t *rev, *wev, **queue;

  10. ngx_connection_t *c;

  11.  
  12. /* NGX_TIMER_INFINITE == INFTIM */

  13.  
  14. ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

  15. "epoll timer: %M", timer);

  16.  
  17. //调用epoll_wait获取已准备就绪的事件

  18. events = epoll_wait(ep, event_list, (int) nevents, timer);

  19.  
  20. err = (events == -1) ? ngx_errno : 0;

  21.  
  22. /***NGX_UPDATE_TIME标识在没有设置timer_resolution配置项时有效表示每次调用epoll_wait函数返回会都更新时间。

  23. ngx_event_timer_alarm变量在设置timer_resolution配置项时有效,每间隔timer_resolution配置项参数值就会设置

  24. ngx_event_timer_alarm变量为1表示需更新时间。***/

  25. if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {

  26. ngx_time_update();

  27. }

  28.  
  29. //err为非零指示epoll_wait失败

  30. if (err) {

  31. if (err == NGX_EINTR) {

  32.  
  33. if (ngx_event_timer_alarm) {

  34. ngx_event_timer_alarm = 0;

  35. return NGX_OK;

  36. }

  37.  
  38. level = NGX_LOG_INFO;

  39.  
  40. } else {

  41. level = NGX_LOG_ALERT;

  42. }

  43.  
  44. ngx_log_error(level, cycle->log, err, "epoll_wait() failed");

  45. return NGX_ERROR;

  46. }

  47.  
  48. if (events == 0) {

  49. if (timer != NGX_TIMER_INFINITE) {

  50. return NGX_OK;

  51. }

  52.  
  53. ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

  54. "epoll_wait() returned no events without timeout");

  55. return NGX_ERROR;

  56. }

  57.  
  58. //仅在多线程环境下此锁才有效

  59. ngx_mutex_lock(ngx_posted_events_mutex);

  60.  
  61. /***循环处理已就绪的事件***/

  62. for (i = 0; i < events; i++) {

  63. //获取事件关联的连接对象,对象地址最低位保存有在事件添加时设置的事件过期位

  64. c = event_list[i].data.ptr;

  65.  
  66. //取事件过期位

  67. instance = (uintptr_t) c & 1;

  68. //屏蔽掉连接对象的最低位

  69. c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);

  70.  
  71. rev = c->read;

  72.  
  73. /***同一条连接的读/写事件的instance位值相同,由于下面先处理读事件这里通过读事件

  74. 的过期位来判断连接是否过期,当fd为-1时也表示连接过期。***/

  75. if (c->fd == -1 || rev->instance != instance) {

  76.  
  77. /*

  78. * the stale event from a file descriptor

  79. * that was just closed in this iteration

  80. */

  81.  
  82. ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

  83. "epoll: stale event %p", c);

  84. continue;

  85. }

  86.  
  87. //获取连接已就绪的事件类型

  88. revents = event_list[i].events;

  89.  
  90. ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

  91. "epoll: fd:%d ev:%04XD d:%p",

  92. c->fd, revents, event_list[i].data.ptr);

  93.  
  94. /***连接出现错误,EPOLLHUP标识表示收到RST报文。检测到这两种类型时 tcp连接中可能还有

  95. 数据未被读取***/

  96. if (revents & (EPOLLERR|EPOLLHUP)) {

  97. ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

  98. "epoll_wait() error on fd:%d ev:%04XD",

  99. c->fd, revents);

  100. }

  101.  
  102. #if 0

  103. if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {

  104. ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,

  105. "strange epoll_wait() events fd:%d ev:%04XD",

  106. c->fd, revents);

  107. }

  108. #endif

  109. /***如果连接发生错误但未置EPOLLIN及EPOLLOUT,这时我们加上EPOLLIN和EPOLLOUT,在调用读/写事件的

  110. 回调函数时就会知道为什么出现错误。 如果不加EPOLLIN和EPOLLOUT,后面就没法调用读/写事件的

  111. 回调函数也就无法处理该连接了。***/

  112. if ((revents & (EPOLLERR|EPOLLHUP))

  113. && (revents & (EPOLLIN|EPOLLOUT)) == 0)

  114. {

  115. /*

  116. * if the error events were returned without EPOLLIN or EPOLLOUT,

  117. * then add these flags to handle the events at least in one

  118. * active handler

  119. */

  120.  
  121. revents |= EPOLLIN|EPOLLOUT;

  122. }

  123.  
  124. /***连接可读且活跃***/

  125. if ((revents & EPOLLIN) && rev->active) {

  126.  
  127. #if (NGX_HAVE_EPOLLRDHUP)

  128. //EPOLLRDHUP表示连接对方关闭了读端

  129. if (revents & EPOLLRDHUP) {

  130. rev->pending_eof = 1;

  131. }

  132. #endif

  133.  
  134. //NGX_POST_THREAD_EVENTS宏末被使用

  135. if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {

  136. rev->posted_ready = 1;

  137.  
  138. } else {

  139. //标识事件已就绪

  140. rev->ready = 1;

  141. }

  142.  
  143. /***NGX_POST_EVENTS表示事件需要延后处理,这里根据accept标识位将事件加入到相应队列中***/

  144. if (flags & NGX_POST_EVENTS) {

  145. queue = (ngx_event_t **) (rev->accept ?

  146. &ngx_posted_accept_events : &ngx_posted_events);

  147.  
  148. ngx_locked_post_event(rev, queue);

  149.  
  150. } else {

  151. //调用事件的回调函数

  152. rev->handler(rev);

  153. }

  154. }

  155.  
  156. wev = c->write;

  157.  
  158. /***连接可写且活跃***/

  159. if ((revents & EPOLLOUT) && wev->active) {

  160.  
  161. //重新检查事件是否过期,因为在处理读事件过程中该事件可能已结束。

  162. if (c->fd == -1 || wev->instance != instance) {

  163.  
  164. /*

  165. * the stale event from a file descriptor

  166. * that was just closed in this iteration

  167. */

  168.  
  169. ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,

  170. "epoll: stale event %p", c);

  171. continue;

  172. }

  173.  
  174. if (flags & NGX_POST_THREAD_EVENTS) {

  175. wev->posted_ready = 1;

  176.  
  177. } else {

  178. wev->ready = 1;

  179. }

  180.  
  181. if (flags & NGX_POST_EVENTS) {

  182. ngx_locked_post_event(wev, &ngx_posted_events);

  183.  
  184. } else {

  185. wev->handler(wev);

  186. }

  187. }

  188. }

  189.  
  190. ngx_mutex_unlock(ngx_posted_events_mutex);

  191.  
  192. return NGX_OK;

  193. }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值