首页 > nginx, 源码分析 > IO复用模型的统一

IO复用模型的统一

2011年9月10日 发表评论 阅读评论 4,317 次浏览

请关注最新修正合订:http://lenky.info/ebook/
这一系列的文章还是在09年写的,存在电脑里很久了,现在贴出来。顺序也不记得了,看到那个就发那个吧,最近都会发上来。欢迎转载,但请保留链接:http://lenky.info/,谢谢。
nginx内统一的IO复用模型一共有8种,分别列表如下:
select:标准的IO复用模型,几乎所有的类unix系统上都有提供,但性能相对较差。如果在当前系统平台找不到更优的IO复用模型,那么nginx默认编译并使用select复用模型。当然,如果当前系统平台提供有更优的IO复用模型,那么我们也可以通过使用–with-select_module或–without-select_module配置选项来启用或禁用select复用模型模块的编译。

poll:也属于标准的IO复用模型,但理论上比select复用模型要优。同select复用模型类似,可以通过使用–with-poll_module或–without-poll_module配置选项来启用或禁用poll复用模型模块的编译。
kqueue:在系统FreeBSD 4.1+,OpenBSD 2.9+,NetBSD 2.0和MacOS X上特有的性能更优秀的IO复用模型。
epoll:系统Linux 2.6+上正式提供的性能更为优秀的IO复用模型。
eventport:在系统Solaris 10上可用的高性能IO复用模型。
/dev/poll:在系统Solaris 7 11/99+,HP/UX 11.22+ (eventport),IRIX 6.5.15+和Tru64 UNIX 5.1A+上可用的高性能IO复用模型。
rtsig:实时信号(real time signals)模型,在Linux 2.2.19+系统上可用。可以通过使用–with-rtsig_module配置选项来启用rtsig模块的编译。
aio:异步IO(Asynchronous Input and Output),通过异步IO函数,如aio_read、aio_write、aio_cancel、aio_error、aio_fsync、aio_return等实现。
nginx提供了如此多的IO复用模型,这里就来看看nginx是如何对这些模型进行统一组织的。
首先,我们知道用户可以通过配置来指定nginx使用的IO复用模型,当然,这需要系统支持指定IO复用模型并且该模块已经被编译,比如我们要指定nginx使用的select复用模型,那么在进行configure编译配置时需要加上–with-select_module选项,这样编译出来的nginx才支持select复用模型,否则你就将获得类似于下面这样的错误:
2009/12/23 21:42:33 [emerg] 31385#0: invalid event type “select” in /usr/local/nginx/conf/nginx.conf:20
通过恰当的修改nginx的配置文件,比如如下所示,就可以使nginx工作在select复用模型下:
#工作模式及连接数上限
events {
use select;
worker_connections      1024;
}
配置选项use的具体解析流程可以参见文档《nginx配置信息的解析流程.doc》内容,这里我们来看下该use选项的实际解析函数ngx_event_use的执行过程。
首先该函数会检测该配置选项use是否有重复,如果有则返回错误字符串:
if (ecf->use != NGX_CONF_UNSET_UINT) {
return “is duplicate”;
}
接着函数依次检测用户设定的复用模型是否存在,这通过比较各个事件模块(复用模型模块都是NGX_EVENT_MODULE类型模块)的名称和用户指定的use选项值,如果找到则保存该模块的ctx_index值,表示这个模块将是nginx使用的IO复用模型模块:
if (module->name->len == value[1].len) {
if (ngx_strcmp(module->name->data, value[1].data) == 0) {
ecf->use = ngx_modules[m]->ctx_index;
ecf->name = module->name->data;

}
}
在这个比较过程中,还有一条规则就是如果nginx是以单进程方式运行的,那么则不允许其运行过程中改变复用模型,如果非要改变只能是restart重启:
if (ngx_process == NGX_PROCESS_SINGLE
&& old_ecf
&& old_ecf->use != ecf->use)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
“when the server runs without a master process ”
“the \”%V\” event type must be the same as ”
“in previous configuration – \”%s\” ”
“and it can not be changed on the fly, ”
“to change it you need to stop server ”
“and start it again”,
&value[1], old_ecf->name);
return NGX_CONF_ERROR;
}
如果函数执行到最后,那么表示用户指定的复用模型系统不支持或者其对应模块未被编译。
对于用户直接或间接选定的复用模型配置,如何实际作用到nginx各个工作进程中去,这个过程如下:
首先,在进程的初始化函数ngx_event_process_init内会进行复用模型模块的查找,这个查找根据前面解析获得的use选项值来进行:
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
continue;        // 非事件模块则直接跳过
}
if (ngx_modules[m]->ctx_index == ecf->use) {  // 对比模块的ctx_index值
module = ngx_modules[m]->ctx;
// 这里的init回调函数是关键所在
if (module->actions.init(cycle, ngx_timer_resolution) == NGX_ERROR)
{
/* fatal */
exit(2);
}
break;
}
}
在找到对应的事件模块之后,就将调用该事件模块的一个初始化函数,进行恰当的全部变量设置。事件模块私有ctx数据结构体的actions字段为ngx_event_actions_t类型,该类型是对IO事件各种相关操作的封装,比如事件的添加、删除、处理等等,而事件模块的初始化函数init函数封装在其内:
typedef struct {
ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t  (*add_conn)(ngx_connection_t *c);
ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
虽然各IO复用模块的初始化函数init函数大有不同,但是它们都会做同一件事情,即将全局变量ngx_event_actions指向自身的actions结构体,因此在这些函数内可以看到这样的赋值语句:
ngx_event_actions = ngx_select_module_ctx.actions;
ngx_event_actions = ngx_poll_module_ctx.actions;
ngx_event_actions = ngx_kqueue_module_ctx.actions;
ngx_event_actions = ngx_epoll_module_ctx.actions;
ngx_event_actions = ngx_eventport_module_ctx.actions;
ngx_event_actions = ngx_devpoll_module_ctx.actions;
ngx_event_actions = ngx_rtsig_module_ctx.actions;
ngx_event_actions = ngx_aio_module_ctx.actions;
同时,在头文件ngx_event.h内可以看到如下宏定义:
#define ngx_process_changes  ngx_event_actions.process_changes
#define ngx_process_events   ngx_event_actions.process_events
#define ngx_done_events      ngx_event_actions.done
#define ngx_add_event        ngx_event_actions.add
#define ngx_del_event        ngx_event_actions.del
#define ngx_add_conn         ngx_event_actions.add_conn
#define ngx_del_conn         ngx_event_actions.del_conn
经过上面的步骤之后,在nginx工作进程内部任意处,当我们调用ngx_add_event时,实际上是调用的被选中的IO复用模块的添加事件函数(比如函数ngx_epoll_add_event),其它函数的调用类似如此。这样看来,nginx对各IO事件模型的统一也还算是容易理解。

转载请保留地址:http://www.lenky.info/archives/2011/09/50http://lenky.info/?p=50


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

分类: nginx, 源码分析 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.