nginx 事件模块初始化

2015-06-24 00:08:12   最后更新: 2015-06-24 00:08:46   访问数量:1134




事件处理框架解决的问题是事件的收集、管理和分发

nginx 事件处理模块主要处理的是网络事件和定时器事件

 

不同的操作系统中,nginx 实现了不同的是事件模块,如在 linux 2.6 以上的平台上,nginx 实现了 epoll 事件模块(ngx_epoll_module),BSD 上实现了 kqueue 事件模块(ngx_kqueue_module),Solaris 上实现了 eventport 事件模块(ngx_eventport_module),当然,其他版本操作系统上大部分实现了 poll 事件模块(ngx_poll_module) 和 select 事件模块(ngx_select_module)

那么,nginx 是如何实现多平台兼容的呢?

如果在配置文件中配置了 events 的相关配置,则在运行初始化方法 ngx_init_cycle 函数时,会读取相应的配置,ngx_events_module 就会以相应的事件模块进行工作

在 worker 进程初始化的过程中,ngx_event_core_module 模块会决定使用哪种事件模型,以及如何管理事件

同时,nginx 提供的各个事件模块具备统一的抽象接口,非常有利于实际的抽象调用

 

我们在 worker 进程初始化 中大致了解了事件模块初始化的过程:

 

// static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) // 事件模块初始化 {{{ static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) { ngx_uint_t m, i; ngx_event_t *rev, *wev; ngx_listening_t *ls; ngx_connection_t *c, *next, *old; ngx_core_conf_t *ccf; ngx_event_conf_t *ecf; ngx_event_module_t *module; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); // 是否是多个进程同时 accept if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) { ngx_use_accept_mutex = 1; ngx_accept_mutex_held = 0; ngx_accept_mutex_delay = ecf->accept_mutex_delay; } else { ngx_use_accept_mutex = 0; } #if (NGX_WIN32) /* * disable accept mutex on win32 as it may cause deadlock if * grabbed by a process which can't accept connections */ ngx_use_accept_mutex = 0; #endif ngx_queue_init(&ngx_posted_accept_events); ngx_queue_init(&ngx_posted_events); // 初始化定时器红黑树 if (ngx_event_timer_init(cycle->log) == NGX_ERROR) { return NGX_ERROR; } for (m = 0; ngx_modules[m]; m++) { // 只有 ngx_event_core_module 和 ngx_epoll_modul // 是 NGX_EVENT_MODULE 类型的 if (ngx_modules[m]->type != NGX_EVENT_MODULE) { continue; } if (ngx_modules[m]->ctx_index != ecf->use) { continue; } module = ngx_modules[m]->ctx; // 初始化 epoll 模块 // 在 src/event/modules/ngx_epoll_module.c 中的 ngx_epoll_init 函数 if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) { /* fatal */ exit(2); } break; } #if !(NGX_WIN32) // 是否设置超时 if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) { struct sigaction sa; struct itimerval itv; ngx_memzero(&sa, sizeof(struct sigaction)); sa.sa_handler = ngx_timer_signal_handler; sigemptyset(&sa.sa_mask); if (sigaction(SIGALRM, &sa, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigaction(SIGALRM) failed"); return NGX_ERROR; } itv.it_interval.tv_sec = ngx_timer_resolution / 1000; itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000; itv.it_value.tv_sec = ngx_timer_resolution / 1000; itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000; if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setitimer() failed"); } } if (ngx_event_flags & NGX_USE_FD_EVENT) { struct rlimit rlmt; if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "getrlimit(RLIMIT_NOFILE) failed"); return NGX_ERROR; } cycle->files_n = (ngx_uint_t) rlmt.rlim_cur; cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n, cycle->log); if (cycle->files == NULL) { return NGX_ERROR; } } #endif // 创建连接池 cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log); if (cycle->connections == NULL) { return NGX_ERROR; } c = cycle->connections; // 创建事件描述结构 cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log); if (cycle->read_events == NULL) { return NGX_ERROR; } rev = cycle->read_events; for (i = 0; i < cycle->connection_n; i++) { rev[i].closed = 1; rev[i].instance = 1; } cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log); if (cycle->write_events == NULL) { return NGX_ERROR; } wev = cycle->write_events; for (i = 0; i < cycle->connection_n; i++) { wev[i].closed = 1; } i = cycle->connection_n; next = NULL; // 初始化连接池 do { i--; c[i].data = next; c[i].read = &cycle->read_events[i]; c[i].write = &cycle->write_events[i]; c[i].fd = (ngx_socket_t) -1; next = &c[i]; #if (NGX_THREADS) c[i].lock = 0; #endif } while (i); cycle->free_connections = next; cycle->free_connection_n = cycle->connection_n; /* for each listening socket */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { c = ngx_get_connection(ls[i].fd, cycle->log); if (c == NULL) { return NGX_ERROR; } c->log = &ls[i].log; c->listening = &ls[i]; ls[i].connection = c; rev = c->read; rev->log = c->log; rev->accept = 1; #if (NGX_HAVE_DEFERRED_ACCEPT) rev->deferred_accept = ls[i].deferred_accept; #endif if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) { if (ls[i].previous) { /* * delete the old accept events that were bound to * the old cycle read events array */ old = ls[i].previous->connection; if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) { return NGX_ERROR; } old->fd = (ngx_socket_t) -1; } } #if (NGX_WIN32) if (ngx_event_flags & NGX_USE_IOCP_EVENT) { ngx_iocp_conf_t *iocpcf; rev->handler = ngx_event_acceptex; if (ngx_use_accept_mutex) { continue; } if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) { return NGX_ERROR; } ls[i].log.handler = ngx_acceptex_log_error; iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module); if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex) == NGX_ERROR) { return NGX_ERROR; } } else { rev->handler = ngx_event_accept; if (ngx_use_accept_mutex) { continue; } if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } } #else // 绑定事件处理函数 rev->handler = ngx_event_accept; if (ngx_use_accept_mutex) { continue; } if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { if (ngx_add_conn(c) == NGX_ERROR) { return NGX_ERROR; } } else { if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } } #endif } return NGX_OK; } // }}}

 

 

上述代码主要做了以下的初始化工作:

  1. 初始化定时器的红黑树
  2. NGX_EVENT_MODULE 模块初始化
  3. 设置超时
  4. 创建连接池
  5. 初始化连接描述结构并加入监听数组

 

 

 

这之中重要的是 NGX_EVENT_MODULE 模块的初始化工作,实现了在各个系统中的多平台兼容,在 linux 中,该模块即 epoll 模块,对应的 module->actions.init 即 ngx_epoll_init,我们在后面再继续讨论这个函数的内容

这之后,nginx 分配并初始化了连接池结构并加入监听数组,准备后续工作

 

在 nginx 事件模块初始化中,最重要的就是 module->actions.init 即 ngx_epoll_init,也就是 epoll 模块初始化

// static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) // epoll 模块初始化 {{{ static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) { ngx_epoll_conf_t *epcf; epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module); if (ep == -1) { // 创建 epoll fd ep = epoll_create(cycle->connection_n / 2); if (ep == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "epoll_create() failed"); return NGX_ERROR; } #if (NGX_HAVE_FILE_AIO) // 使用异步IO ngx_epoll_aio_init(cycle, epcf); #endif } if (nevents < epcf->events) { if (event_list) { ngx_free(event_list); } event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events, cycle->log); if (event_list == NULL) { return NGX_ERROR; } } nevents = epcf->events; ngx_io = ngx_os_io; // 初始化事件添加、删除、启用、禁用、事件分发等回调函数 ngx_event_actions = ngx_epoll_module_ctx.actions; #if (NGX_HAVE_CLEAR_EVENT) // 边缘触发 ngx_event_flags = NGX_USE_CLEAR_EVENT #else // 水平触发 ngx_event_flags = NGX_USE_LEVEL_EVENT #endif |NGX_USE_GREEDY_EVENT |NGX_USE_EPOLL_EVENT; return NGX_OK; } // }}}

 

 

关于 epoll 的使用可以参看:

epoll 的使用

 

正如上面链接中博客的描述,epoll 模块初始化主要进行了 epoll 的 epoll_create 操作,并设置为边沿触发或水平触发,关于这两种触发方式的区别,也可以参看上面链接中的博客内容

但是在 epoll 模块中有一个重要的函数 ngx_epoll_aio_init,将 fd 设置为异步 IO 方式,该函数的具体内容请参看下一篇博客

 






读书笔记      技术帖      epoll      nginx      源码      opensource      init      初始化      sourcecode      事件      模块      开源      events      event      ngx_epoll_module      ngx_event_process_init      ngx_epoll_init      ngx_epoll_aio_init     


京ICP备15018585号