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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384969.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Inotify机制

描述 Inotify API用于检测文件系统变化的机制。Inotify可用于检测单个文件&#xff0c;也可以检测整个目录。当检测的对象是一个目录的时候&#xff0c;目录本身和目录里的内容都会成为检测的对象。 此种机制的出现的目的是当内核空间发生某种事件之后&#xff0c;可以立即通…

文件操作(二进制文件加密解密)

加密 #include<stdio.h> #include<string.h>void code(char *p,size_t n) {size_t i;for(i 0; i < n; i){p[i] 3;} }int main() {FILE *p1 fopen("./a.txt","r");FILE *p2 fopen("./b.txt","w");char buf[1024] {…

北京加密机现场select问题

问题描述 北京项目通过调用我们提供的库libsigxt.a与加密机通信&#xff0c;c/s架构&#xff0c;客户端启用多个线程&#xff0c;每个线程流程有以下三步&#xff0c;连接加密机&#xff0c;签名&#xff0c;关闭链接。在正常运行一段时间后会出现不能连接加密机服务问题。 连…

拼接字符串(带参程序)

1.用strcat拼接函数可以实现 #include<stdio.h> #include<string.h>int main(int argc,char ** argv) {char str[100] {0};int i;for( i 1; i < argc; i){strcat(str,argv[i]);}printf("str %s\n",str);return 0; } 2.用sprintf函数也可以实现 #in…

详细解释signal和sigaction以及SIG_BLOCK

signal&#xff0c;此函数相对简单一些&#xff0c;给定一个信号&#xff0c;给出信号处理函数则可&#xff0c;当然&#xff0c;函数简单&#xff0c;其功能也相对简单许多&#xff0c;简单给出个函数例子如下&#xff1a; [cpp] view plain copy 1 #include <signal.h>…

处理SIGCHLD信号

在上一讲中&#xff0c;我们使用fork函数得到了一个简单的并发服务器。然而&#xff0c;这样的程序有一个问题&#xff0c;就是当子进程终止时&#xff0c;会向父进程发送一个SIGCHLD信号&#xff0c;父进程默认忽略&#xff0c;导致子进程变成一个僵尸进程。僵尸进程一定要处理…

文件操作(stat)

/*** stat.c ***/ #include<stdio.h> #include<string.h> #include<sys/stat.h> #include<stdlib.h>int main() {struct stat st {0}; //定义一个结构体&#xff0c;名字叫ststat("./a.txt",&st); //调用完stat函数之后&…

nginx源码阅读(一).综述

前言 nginx作为一款开源的轻量级高性能web服务器,是非常值得立志从事服务端开发方向的人学习的。现今nginx的最新版本是nginx-1.13.6,代码量也日渐庞大,但是由于其核心思想并没改变,为了降低阅读难度,我选择的是nginx-1.0.15版本,并且由于时间和水平有限,重点关注的是nginx的启…

文件操作(stat函数)

stat函数可以获取文件信息 /*** stat.c ***/ #include<stdio.h> #include<string.h> #include<sys/stat.h> #include<stdlib.h>int main() {struct stat st {0}; //定义一个结构体&#xff0c;名字叫ststat("./a.txt",&st); …

文件操作(结构体)

将结构体内容写入到文件中 #include<stdio.h> #include<string.h>struct student {char name[100];int age; };int main() {struct student st {"wangqinghe",30};FILE * p fopen("./c.txt","wb");fwrite(&st,sizeof(st),1,p…

nginx源码阅读(二).初始化:main函数及ngx_init_cycle函数

前言 在分析源码时,我们可以先把握主干,然后其他部分再挨个分析就行了。接下来我们先看看nginx的main函数干了些什么。 main函数 这里先介绍一些下面会遇到的变量类型: ngx_int_t: typedef intptr_t ngx_int_t; 64位机器上,intptr_t为long int, 即typedef long int intptr_…

EAGAIN、EWOULDBLOCK、EINTR与非阻塞

EWOULDBLOCK&#xff1a;用于非阻塞模式&#xff0c;不需要重新读或者写 EINTR&#xff1a;指操作被中断唤醒&#xff0c;需要重新读/写   在Linux环境下开发经常会碰到很多错误(设置errno)&#xff0c;其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。 从字面上…

文件操作(排序)

文本文件&#xff0c;每行代表一个整数&#xff0c;范围在0~512之间&#xff1b; 要求&#xff1a;对文件排序&#xff0c;不使用堆空间&#xff0c;只使用栈空间。 用srand()和rand()函数生成一定量的随机数 /*** file.c ***/ #include<stdio.h> #include<string.h&g…

Linux下send错误代码32

问题描述&#xff1a;今天写程序&#xff0c;socket后send出现这个问题&#xff0c;send的返回值为-1&#xff0c;而errno为32&#xff0c;这个错误代码为broken pipe&#xff0c;即管道破裂。 问题形成原因&#xff1a;后来通过排查研究&#xff0c;发现出现该种问题出现的可能…

系统级性能分析工具perf的介绍与使用

系统级性能优化通常包括两个阶段&#xff1a;性能剖析&#xff08;performance profiling&#xff09;和代码优化。 性能剖析的目标是寻找性能瓶颈&#xff0c;查找引发性能问题的原因及热点代码。 代码优化的目标是针对具体性能问题而优化代码或编译选项&#xff0c;以改善软…

linux C线程

一个应用程序可以启动若干个线程&#xff1b;线程&#xff0c;是程序执行的最小单位&#xff1b;一般一个最简单的程序最少有一个线程&#xff0c;就是程序本身&#xff0c;也是主函数&#xff1b;一个线程阻塞不会影响另一个线程&#xff1b;多线程的进程可以尽可能多的利用系…

fseek函数

fseek函数&#xff1a; int fseek(FILE *_FILE, long _Offset, int _Origin); 函数设置文件指针stream的位置&#xff0c;如果执行成功&#xff0c;stream将指向以fromwhere为基准&#xff0c;偏移量offset&#xff08;指针偏移量&#xff09;个字节的位置&#xff0c;函数返回…

linux ethtool 查看网卡状态

ethtool 工具关于网络协商功能介绍&#xff1b; ethtool - Display or change ethernet card settings&#xff08;ethtool 是用来显示和更改网卡设置的工具&#xff09;&#xff1b;这个工具比较复杂&#xff0c;功能也特别多。由于洋文比较难懂。所以我们还是把网络设备协商…

ftell函数

ftell函数用于得到文件位置指针当前位置相对于文件首的偏移字节数&#xff0c;在随机方式存储文件时&#xff0c;由于文件位置频繁的前后移动&#xff0c;程序不容易确定文件的当前位置。 /*** a.txt ***/ asd gsdert dfhjtew /*** ftell.c ***/ #include<stdio.h>int ma…

muduo网络库源码阅读Step by Step

Posted on: Nov 26 2015 Categories: muduo C Tags: muduo 一般写服务端程序都需要有一个称手的网络库来帮我们处理琐碎的网络通信细节&#xff0c;比如连接的建立、关闭&#xff0c;读取数据&#xff0c;发送数据&#xff0c;接收、发送缓冲区的管理等&#xff0c;常用的C/C网…