Nginx源码分析-Epoll模块
浏览:3953次 出处信息
Linux平台上,Nginx使用epoll完成事件驱动,实现高并发;本文将不对epoll本身进行介绍(网上一堆一堆的文章介绍epoll的原理及使用方法,甚至源码分析等),仅看一下Nginx是如何使用epoll的。
Nginx在epoll模块中定义了好几个函数,这些函数基本都是作为回调注册到事件抽象层的对应接口上,从而实现了事件驱动的具体化,我们看如下的一段代码:
ngx_event_module_t ngx_epoll_module_ctx = { &epoll_name, ngx_epoll_create_conf, /* create configuration */ ngx_epoll_init_conf, /* init configuration */ { ngx_epoll_add_event, /* add an event */ ngx_epoll_del_event, /* delete an event */ ngx_epoll_add_event, /* enable an event */ ngx_epoll_del_event, /* disable an event */ ngx_epoll_add_connection, /* add an connection */ ngx_epoll_del_connection, /* delete an connection */ NULL, /* process the changes */ ngx_epoll_process_events, /* process the events */ ngx_epoll_init, /* init the events */ ngx_epoll_done, /* done the events */ } };
这段代码就是epoll的相关函数注册到事件抽象层,这里所谓的事件抽象层在前面的博文中有提过,就是Nginx为了方便支持和开发具体的I/O模型,从而实现的一层抽象。代码后面的注释将功能说明得很详细了,本文就只重点关注ngx_epoll_init和ngx_epoll_process_events两个函数,其他几个函数就暂且忽略了。
ngx_epoll_init主要是完成epoll的相关初始化工作,代码分析如下:
static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) { ngx_epoll_conf_t *epcf; /*取得epoll模块的配置结构*/ epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module); /*ep是epoll模块定义的一个全局变量,初始化为-1*/ if (ep == -1) { /*创一个epoll对象,容量为总连接数的一半*/ 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; } } /*nevents也是epoll模块定义的一个全局变量,初始化为0*/ if (nevents events) { if (event_list) { ngx_free(event_list); } /*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_is定义为: ngx_os_io_t ngx_os_io = { ngx_unix_recv, ngx_readv_chain, ngx_udp_unix_recv, ngx_unix_send, ngx_writev_chain, 0 };(位于src/os/unix/ngx_posix_init.c) */ ngx_io = ngx_os_io; /*这里就是将epoll的具体接口函数注册到事件抽象层接口ngx_event_actions上。 具体是上文提到的ngx_epoll_module_ctx中封装的如下几个函数 ngx_epoll_add_event, ngx_epoll_del_event, ngx_epoll_add_event, ngx_epoll_del_event, ngx_epoll_add_connection, ngx_epoll_del_connection, ngx_epoll_process_events, ngx_epoll_init, ngx_epoll_done, */ ngx_event_actions = ngx_epoll_module_ctx.actions; #if (NGX_HAVE_CLEAR_EVENT) /*epoll将添加这个标志,主要为了实现边缘触发*/ ngx_event_flags = NGX_USE_CLEAR_EVENT #else /*水平触发*/ ngx_event_flags = NGX_USE_LEVEL_EVENT #endif |NGX_USE_GREEDY_EVENT /*io的时候,直到EAGAIN为止*/ |NGX_USE_EPOLL_EVENT; /*epoll标志*/ return NGX_OK; }
epoll初始化工作没有想象中的复杂,和我们平时使用epoll都一样,下面看ngx_epoll_process_events,这个函数主要用来完成事件的等待并处理。
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { int events; uint32_t revents; ngx_int_t instance, i; ngx_uint_t level; ngx_err_t err; ngx_log_t *log; ngx_event_t *rev, *wev, **queue; ngx_connection_t *c; /*一开始就是等待事件,最长等待时间为timer;nginx为事件 专门用红黑树维护了一个计时器。后续对这个timer单独分析。 */ events = epoll_wait(ep, event_list, (int) nevents, timer); if (events == -1) { err = ngx_errno; } else { err = 0; } if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { /*执行一次时间更新, nginx将时间缓存到了一组全局变量中,方便程序高效的获取事件。*/ ngx_time_update(); } /*处理wait错误*/ if (err) { if (err == NGX_EINTR) { if (ngx_event_timer_alarm) { ngx_event_timer_alarm = 0; return NGX_OK; } level = NGX_LOG_INFO; } else { level = NGX_LOG_ALERT; } ngx_log_error(level, cycle->log, err, "epoll_wait() failed"); return NGX_ERROR; } /*wait返回事件数0,可能是timeout返回,也可能是非timeout返回;非timeout返回则是error*/ if (events == 0) { if (timer != NGX_TIMER_INFINITE) { return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "epoll_wait() returned no events without timeout"); return NGX_ERROR; } log = cycle->log; /*for循环开始处理收到的所有事件*/ for (i = 0; i read; 。。。。。。。。。。。。。 /*取得发生一个事件*/ revents = event_list[i].events; /*记录wait的错误返回状态*/ if (revents & (EPOLLERR|EPOLLHUP)) { ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, "epoll_wait() error on fd:%d ev:%04XD", c->fd, revents); } if ((revents & (EPOLLERR|EPOLLHUP)) && (revents & (EPOLLIN|EPOLLOUT)) == 0) { /* * if the error events were returned without EPOLLIN or EPOLLOUT, * then add these flags to handle the events at least in one * active handler */ revents |= EPOLLIN|EPOLLOUT; } /*该事件是一个读事件,并该连接上注册的读事件是active的*/ if ((revents & EPOLLIN) && rev->active) { if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) { rev->posted_ready = 1; } else { rev->ready = 1; } /*事件放入相应的队列中;关于此处的先入队再处理,在前面的文章中已经介绍过了。*/ if (flags & NGX_POST_EVENTS) { queue = (ngx_event_t **) (rev->accept ? &ngx_posted_accept_events : &ngx_posted_events); ngx_locked_post_event(rev, queue); /*入队*/ } else { rev->handler(rev); } } wev = c->write; /*发生的是一个写事件,和读事件完全一样的逻辑过程*/ if ((revents & EPOLLOUT) && wev->active) { if (flags & NGX_POST_THREAD_EVENTS) { wev->posted_ready = 1; } else { wev->ready = 1; } /*先入队再处理*/ if (flags & NGX_POST_EVENTS) { ngx_locked_post_event(wev, &ngx_posted_events); } else { wev->handler(wev); } } } return NGX_OK; }
本文将关注的两个epoll函数也就这么一点代码了,但整个epoll还有添加事件和删除事件等的相关函数,代码都很简单,本文就不做具体的分析了。
建议继续学习:
- Nginx源码分析-事件循环 (阅读:4913)
- Hive的入口 -- Hive源码解析 (阅读:4799)
- Storm源码浅析之topology的提交 (阅读:4400)
- Hive源码解析-之-语法解析器 (阅读:4283)
- Nginx源码分析-内存池 (阅读:4173)
- Lua GC 的源码剖析 (2) (阅读:3906)
- Lua GC 的源码剖析 (4) (阅读:3441)
- redis源代码分析 - event library (阅读:3176)
- Redis的事件循环与定时器模型 (阅读:3113)
- ExtJS源码研究笔记之总评 (阅读:3076)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
扫一扫订阅我的微信号:IT技术博客大学习
<< 前一篇:timetunnel之系统架构
后一篇:Web性能优化中的CPP方法 >>
文章信息
- 作者:yixiao 来源: 淘宝数据平台与产品部官方博客 tbdata.org
- 标签: Epoll 源码
- 发布时间:2010-12-26 20:58:23
建议继续学习
近3天十大热文
- [56] IOS安全–浅谈关于IOS加固的几种方法
- [55] Oracle MTS模式下 进程地址与会话信
- [54] 图书馆的世界纪录
- [54] android 开发入门
- [52] 如何拿下简短的域名
- [51] Go Reflect 性能
- [50] 【社会化设计】自我(self)部分――欢迎区
- [47] 读书笔记-壹百度:百度十年千倍的29条法则
- [37] 程序员技术练级攻略
- [31] 视觉调整-设计师 vs. 逻辑