libevent源码深度剖析十

——支持I/O多路复用技术
张亮

Libevent的核心是事件驱动、同步非阻塞,为了达到这一目标,必须采用系统提供的I/O多路复用技术,而这些在Windows、Linux、
Unix等不同平台上却各有不同,如何能提供优雅而统一的支持方式,是首要关键的问题,这其实不难,本节就来分析一下。

1 统一的关键

Libevent支持多种I/O多路复用技术的关键就在于结构体eventop,这个结构体前面也曾提到过,它的成员是一系列的函数指针, 定义在event-internal.h文件中:

  1. struct eventop {
  2. const char *name;
  3. void *(*init)(struct event_base *); // 初始化
  4. int (*add)(void *, struct event *); // 注册事件
  5. int (*del)(void *, struct event *); // 删除事件
  6. int (*dispatch)(struct event_base *, void *, struct timeval *); // 事件分发
  7. void (*dealloc)(struct event_base *, void *); // 注销,释放资源
  8. /* set if we need to reinitialize the event base */
  9. int need_reinit;
  10. };

在libevent中,每种I/O demultiplex机制的实现都必须提供这五个函数接口,来完成自身的初始化、销毁释放;对事件的注册、注销和分发。
比如对于epoll,libevent实现了5个对应的接口函数,并在初始化时并将eventop的5个函数指针指向这5个函数,那么程序就可以使用epoll作为I/O demultiplex机制了。

2 设置I/O demultiplex机制

Libevent把所有支持的I/O demultiplex机制存储在一个全局静态数组eventops中,并在初始化时选择使用何种机制,数组内容根据优先级顺序声明如下:

  1. /* In order of preference */
  2. static const struct eventop *eventops[] = {
  3. #ifdef HAVE_EVENT_PORTS
  4. &evportops,
  5. #endif
  6. #ifdef HAVE_WORKING_KQUEUE
  7. &kqops,
  8. #endif
  9. #ifdef HAVE_EPOLL
  10. &epollops,
  11. #endif
  12. #ifdef HAVE_DEVPOLL
  13. &devpollops,
  14. #endif
  15. #ifdef HAVE_POLL
  16. &pollops,
  17. #endif
  18. #ifdef HAVE_SELECT
  19. &selectops,
  20. #endif
  21. #ifdef WIN32
  22. &win32ops,
  23. #endif
  24. NULL
  25. };

然后libevent根据系统配置和编译选项决定使用哪一种I/O demultiplex机制,这段代码在函数event_base_new()中:

  1. base->evbase = NULL;
  2. for (i = 0; eventops[i] && !base->evbase; i++) {
  3. base->evsel = eventops[i];
  4. base->evbase = base->evsel->init(base);
  5. }

可以看出,libevent在编译阶段选择系统的I/O demultiplex机制,而不支持在运行阶段根据配置再次选择。
    以Linux下面的epoll为例,实现在源文件epoll.c中,eventops对象epollops定义如下:

  1. const struct eventop epollops = {
  2. "epoll",
  3. epoll_init,
  4. epoll_add,
  5. epoll_del,
  6. epoll_dispatch,
  7. epoll_dealloc,
  8. 1 /* need reinit */
  9. };

变量epollops中的函数指针具体声明如下,注意到其返回值和参数都和eventop中的定义严格一致,这是函数指针的语法限制。

  1. static void *epoll_init    (struct event_base *);
  2. static int epoll_add    (void *, struct event *);
  3. static int epoll_del    (void *, struct event *);
  4. static int epoll_dispatch(struct event_base *, void *, struct timeval *);
  5. 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源码深度剖析十的更多相关文章

  1. libevent源码深度剖析十二

    libevent源码深度剖析十二 ——让libevent支持多线程 张亮 Libevent本身不是多线程安全的,在多核的时代,如何能充分利用CPU的能力呢,这一节来说说如何在多线程环境中使用libev ...

  2. libevent 源码深度剖析十三

    libevent 源码深度剖析十三 —— libevent 信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多 ...

  3. libevent源码深度剖析十一

    libevent源码深度剖析十一 ——时间管理 张亮 为了支持定时器,Libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数.时间缓存.时间校正和定时器堆的时间值 ...

  4. libevent源码深度剖析九

    libevent源码深度剖析九 ——集成定时器事件 张亮 现在再来详细分析libevent中I/O事件和Timer事件的集成,与Signal相比,Timer事件的集成会直观和简单很多.Libevent ...

  5. libevent源码深度剖析八

    libevent源码深度剖析八 ——集成信号处理 张亮 现在我们已经了解了libevent的基本框架:事件管理框架和事件主循环.上节提到了libevent中I/O事件和Signal以及Timer事件的 ...

  6. libevent源码深度剖析七

    libevent源码深度剖析七 ——事件主循环 张亮 现在我们已经初步了解了libevent的Reactor组件——event_base和事件管理框架,接下来就是libevent事件处理的中心部分 — ...

  7. libevent源码深度剖析六

    libevent源码深度剖析六 ——初见事件处理框架 张亮 前面已经对libevent的事件处理框架和event结构体做了描述,现在是时候剖析libevent对事件的详细处理流程了,本节将分析 lib ...

  8. libevent源码深度剖析五

    libevent源码深度剖析五 ——libevent的核心:事件event 张亮 对事件处理流程有了高层的认识后,本节将详细介绍libevent的核心结构event,以及libevent对event的 ...

  9. libevent源码深度剖析四

    libevent源码深度剖析四 ——libevent源代码文件组织 1 前言 详细分析源代码之前,如果能对其代码文件的基本结构有个大概的认识和分类,对于代码的分析将是大有裨益的.本节内容不多,我想并不 ...

随机推荐

  1. 余弦相似性计算及python代码实现

    A:西米喜欢健身 B:超超不爱健身,喜欢打游戏 step1:分词 A:西米/喜欢/健身 B:超超/不/喜欢/健身,喜欢/打/游戏 step2:列出两个句子的并集 西米/喜欢/健身/超超/不/打/游戏 ...

  2. c++ wchar_t 与char 直接的转换【转】

    http://blog.163.com/tianshi_17th/blog/static/4856418920085209414977/ 实现了一下 #include "stdafx.h&q ...

  3. INIT: vesion 2.88 booting

    /***************************************************************************** * INIT: vesion 2.88 b ...

  4. 数据结构之最小生成树Prim算法

    普里姆算法介绍 普里姆(Prim)算法,是用来求加权连通图的最小生成树算法 基本思想:对于图G而言,V是所有顶点的集合:现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最 ...

  5. linux之管道

    1. 进程间通信概述 进程是一个独立的资源分配单元,不同进程之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源.进程不是孤立的,不同的进程需要进行信息的交互和状态的传递等,因此需要 ...

  6. 阿里云部署django实现公网访问

    本博的主要目的是对阿里云部署django实现公网访问进行一次简单的记录,方便日后查询. 内容目录: (1)申请阿里云服务器及安全组配置 (2)实现ssh远程控制 (3)实现ftp文件传输 (4)安装p ...

  7. SqlServer 数据库读写分离【转】

    1. 实现原理:读写分离简单的说是把对数据库读和写的操作分开对应不同的数据库服务器,这样能有效地减轻数据库压力,也能减轻io压力.主数据库提供写操作,从数据库提供读操作,其实在很多系统中,主要是读的操 ...

  8. LibreOJ β Round #2 题解

    LibreOJ β Round #2 题解 模拟只会猜题意 题目: 给定一个长为 \(n\) 的序列,有 \(m\) 次询问,每次问所有长度大于 \(x\) 的区间的元素和的最大值. \(1 \leq ...

  9. 洛谷P2835 刻录光盘

    传送门 题目大意:有光盘可以传着看,问最少从哪几个人分发,能全部传一遍. 题解:缩点后求入度为0的点的个数 代码: #include<iostream> #include<cstdi ...

  10. jquery移除、绑定、触发元素事件

    unbind(type [,data]) //data是要移除的函数 $('#btn').unbind("click"); //移除click $('#btn').unbind() ...