libevent源码深度剖析十
libevent源码深度剖析十
——支持I/O多路复用技术
张亮
Libevent的核心是事件驱动、同步非阻塞,为了达到这一目标,必须采用系统提供的I/O多路复用技术,而这些在Windows、Linux、
Unix等不同平台上却各有不同,如何能提供优雅而统一的支持方式,是首要关键的问题,这其实不难,本节就来分析一下。
1 统一的关键
Libevent支持多种I/O多路复用技术的关键就在于结构体eventop,这个结构体前面也曾提到过,它的成员是一系列的函数指针, 定义在event-internal.h文件中:
- struct eventop {
- const char *name;
- void *(*init)(struct event_base *); // 初始化
- int (*add)(void *, struct event *); // 注册事件
- int (*del)(void *, struct event *); // 删除事件
- int (*dispatch)(struct event_base *, void *, struct timeval *); // 事件分发
- void (*dealloc)(struct event_base *, void *); // 注销,释放资源
- /* set if we need to reinitialize the event base */
- int need_reinit;
- };
在libevent中,每种I/O demultiplex机制的实现都必须提供这五个函数接口,来完成自身的初始化、销毁释放;对事件的注册、注销和分发。
比如对于epoll,libevent实现了5个对应的接口函数,并在初始化时并将eventop的5个函数指针指向这5个函数,那么程序就可以使用epoll作为I/O demultiplex机制了。
2 设置I/O demultiplex机制
Libevent把所有支持的I/O demultiplex机制存储在一个全局静态数组eventops中,并在初始化时选择使用何种机制,数组内容根据优先级顺序声明如下:
- /* In order of preference */
- static const struct eventop *eventops[] = {
- #ifdef HAVE_EVENT_PORTS
- &evportops,
- #endif
- #ifdef HAVE_WORKING_KQUEUE
- &kqops,
- #endif
- #ifdef HAVE_EPOLL
- &epollops,
- #endif
- #ifdef HAVE_DEVPOLL
- &devpollops,
- #endif
- #ifdef HAVE_POLL
- &pollops,
- #endif
- #ifdef HAVE_SELECT
- &selectops,
- #endif
- #ifdef WIN32
- &win32ops,
- #endif
- NULL
- };
然后libevent根据系统配置和编译选项决定使用哪一种I/O demultiplex机制,这段代码在函数event_base_new()中:
- base->evbase = NULL;
- for (i = 0; eventops[i] && !base->evbase; i++) {
- base->evsel = eventops[i];
- base->evbase = base->evsel->init(base);
- }
可以看出,libevent在编译阶段选择系统的I/O demultiplex机制,而不支持在运行阶段根据配置再次选择。
以Linux下面的epoll为例,实现在源文件epoll.c中,eventops对象epollops定义如下:
- const struct eventop epollops = {
- "epoll",
- epoll_init,
- epoll_add,
- epoll_del,
- epoll_dispatch,
- epoll_dealloc,
- 1 /* need reinit */
- };
变量epollops中的函数指针具体声明如下,注意到其返回值和参数都和eventop中的定义严格一致,这是函数指针的语法限制。
- static void *epoll_init (struct event_base *);
- static int epoll_add (void *, struct event *);
- static int epoll_del (void *, struct event *);
- static int epoll_dispatch(struct event_base *, void *, struct timeval *);
- static void epoll_dealloc (struct event_base *, void *);
那么如果选择的是epoll,那么调用结构体eventop的init和dispatch函数指针时,实际调用的函数就是epoll的初始化函数epoll_init()和事件分发函数epoll_dispatch()了;
关于epoll的具体用法这里就不多说了,可以参见介绍epoll的文章(本人的哈哈):
http://blog.csdn.net/sparkliang/archive/2009/11/05/4770655.aspx
C++语言提供了虚函数来实现多态,在C语言中,这是通过函数指针实现的。对于各类函数指针的详细说明可以参见文章:
http://blog.csdn.net/sparkliang/archive/2009/06/09/4254115.aspx
同样的,上面epollops以及epoll的各种函数都直接定义在了epoll.c源文件中,对外都是不可见的。对于libevent的使用者而言,完全不会知道它们的存在,对epoll的使用也是通过eventop来完成的,达到了信息隐藏的目的。
3 小节
支持多种I/O demultiplex机制的方法其实挺简单的,借助于函数指针就OK了。通过对源代码的分析也可以看出,Libevent是在编译阶段选择系统的I/O demultiplex机制的,而不支持在运行阶段根据配置再次选择。
libevent源码深度剖析十的更多相关文章
- libevent源码深度剖析十二
libevent源码深度剖析十二 ——让libevent支持多线程 张亮 Libevent本身不是多线程安全的,在多核的时代,如何能充分利用CPU的能力呢,这一节来说说如何在多线程环境中使用libev ...
- libevent 源码深度剖析十三
libevent 源码深度剖析十三 —— libevent 信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多 ...
- libevent源码深度剖析十一
libevent源码深度剖析十一 ——时间管理 张亮 为了支持定时器,Libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数.时间缓存.时间校正和定时器堆的时间值 ...
- libevent源码深度剖析九
libevent源码深度剖析九 ——集成定时器事件 张亮 现在再来详细分析libevent中I/O事件和Timer事件的集成,与Signal相比,Timer事件的集成会直观和简单很多.Libevent ...
- libevent源码深度剖析八
libevent源码深度剖析八 ——集成信号处理 张亮 现在我们已经了解了libevent的基本框架:事件管理框架和事件主循环.上节提到了libevent中I/O事件和Signal以及Timer事件的 ...
- libevent源码深度剖析七
libevent源码深度剖析七 ——事件主循环 张亮 现在我们已经初步了解了libevent的Reactor组件——event_base和事件管理框架,接下来就是libevent事件处理的中心部分 — ...
- libevent源码深度剖析六
libevent源码深度剖析六 ——初见事件处理框架 张亮 前面已经对libevent的事件处理框架和event结构体做了描述,现在是时候剖析libevent对事件的详细处理流程了,本节将分析 lib ...
- libevent源码深度剖析五
libevent源码深度剖析五 ——libevent的核心:事件event 张亮 对事件处理流程有了高层的认识后,本节将详细介绍libevent的核心结构event,以及libevent对event的 ...
- libevent源码深度剖析四
libevent源码深度剖析四 ——libevent源代码文件组织 1 前言 详细分析源代码之前,如果能对其代码文件的基本结构有个大概的认识和分类,对于代码的分析将是大有裨益的.本节内容不多,我想并不 ...
随机推荐
- 互联网公司面试必问的mysql题目(上)
又到了招聘的旺季,被要求准备些社招.校招的题库.(如果你是应届生,尤其是东北的某大学,绝对福利哦) 介绍:MySQL是一个关系型数据库管理系统,目前属于 Oracle 旗下产品.虽然单机性能比不上or ...
- JVM年轻代(young generation)老年代(old generation tenured)持久代(permanent generation)GC
关于jvm内存代,看到这篇文章,转发下 链接地址 ---多谢 虚拟机中的共划分为三个代:年轻代(Young Generation).老年代(Old Generation)和持久代(Permanent ...
- upper_bound函数,binary_check函数
个人心得:二分的经典运用,刚开始就是upper_bound可能难以实现一点,还有就是要注意没找到的时候 lower_bound 返回大于等于key的第一个元素的下标.upper_bound 返回大于k ...
- matlab滤波器的设计
求出濾波器的階數以及 3dB 截止頻率後,可用相應的 Matlab 函數計算出實現傳遞函數的分子分母係數來.巴特沃斯型濾波器是通帶內最大平坦.帶外單調下降型的,其計算命令是:[b,a] = butte ...
- jQuery火箭图标返回顶部代码
在网上找来段使用jQuery火箭图标返回顶部代码,感觉比较酷,比较炫,大概样式如下, 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1. ...
- 使用 key 登录时分开记录操作历史记录
线上服务器一般都是配置 key 登录,一个账号可以多个工作人员连接,操作命令历史却全部记录在一个文件中,当然后查看某条命令是谁执行的时候就不好查了.这时候我们就可以通过配置 histroy 相关环境变 ...
- erlang热部署
以下流程参考rebar的wiki,亲测 rebar的版本一定要注意,高版本对于下面两个指令有bug rebar generate-appups rebar generate-upgrade 经过一个个 ...
- 1125 Chain the Ropes
题意:略. 思路:思考一下,最先拿去对折的绳子会参与之后的每次对折,而对一条绳子而言,对折的次数越多剩下的就越短,因此,要让最终的结果尽可能长,应该先让较短的绳子先对折. 代码: #include & ...
- ejs的简单应用
获取数据 var init = function(){ api.my_bean_list({ },function(datas){ console.log(datas); if (datas.errn ...
- djano-cbv模式
cbv,class base view 就是用类写视图 详细用法替换fbv将在后续空余时间上补齐 fbv,function base view 就是用函数写视图 创建django app01项目 ur ...