libevent源码深度剖析十

libevent源码深度剖析十


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

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

1 统一的关键

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

[cpp] view plaincopy

  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中,并在初始化时选择使用何种机制,数组内容根据优先级顺序声明如下:

[cpp] view plaincopy

  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()中:

[cpp] view plaincopy

  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定义如下:

[cpp] view plaincopy

  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中的定义严格一致,这是函数指针的语法限制。

[cpp] view plaincopy

  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机制的,而不支持在运行阶段根据配置再次选择。

时间: 2024-08-07 00:10:41

libevent源码深度剖析十的相关文章

libevent源码深度剖析十二

libevent源码深度剖析十二 --让libevent支持多线程张亮 Libevent本身不是多线程安全的,在多核的时代,如何能充分利用CPU的能力呢,这一节来说说如何在多线程环境中使用libevent,跟源代码并没有太大的关系,纯粹是使用上的技巧. 1 错误使用示例 在多核的CPU上只使用一个线程始终是对不起CPU的处理能力啊,那好吧,那就多创建几个线程,比如下面的简单服务器场景.1 主线程创建工作线程1:2 接着主线程监听在端口上,等待新的连接:3 在线程1中执行event事件循环,等待事

libevent源码深度剖析

libevent 源码深度剖析,from: blog.csdn.net/sparkliang/article/category/660506 http://download.csdn.net/detail/sparkliang/2001038#comment http://libevent.org/

libevent 源码深度剖析十三

libevent 源码深度剖析十三 -- libevent 信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多个 libevent 实例上注册信号事件.依然冠名追加到 libevent 系列. 以 2 个线程为例,做简单的场景分析. 1 首先是创建并初始化线程 1 的 libevent 实例 base1 ,线程 1 的 libevent 实例 base2 : 2 在 base1 上注册 SIGALRM 信号

libevent源码深度剖析二

libevent源码深度剖析二 --Reactor模式 张亮 前面讲到,整个libevent本身就是一个Reactor,因此本节将专门对Reactor模式进行必要的介绍,并列出libevnet中的几个重要组件和Reactor的对应关系,在后面的章节中可能还会提到本节介绍的基本概念. 1 Reactor的事件处理机制 首先来回想一下普通函数调用的机制:程序调用某函数?函数执行,程序等待?函数将结果和控制权返回给程序?程序继续处理. Reactor释义"反应堆",是一种事件驱动机制.和普通

libevent源码深度剖析四

libevent源码深度剖析四 --libevent源代码文件组织 1 前言 详细分析源代码之前,如果能对其代码文件的基本结构有个大概的认识和分类,对于代码的分析将是大有裨益的.本节内容不多,我想并不是说它不重要! 2 源代码组织结构 Libevent的源代码虽然都在一层文件夹下面,但是其代码分类还是相当清晰的,主要可分为头文件.内部使用的头文件.辅助功能函数.日志. libevent框架.对系统I/O多路复用机制的封装.信号管理.定时事件管理.缓冲区管理.基本数据结构和基于libevent的两

libevent源码深度剖析五

libevent源码深度剖析五--libevent的核心:事件event 张亮 对事件处理流程有了高层的认识后,本节将详细介绍libevent的核心结构event,以及libevent对event的管理. 1 libevent的核心-event Libevent是基于事件驱动(event-driven)的,从名字也可以看到event是整个库的核心.event就是Reactor框架中的事件 处理程序组件:它提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效

libevent源码深度剖析十一

libevent源码深度剖析十一 --时间管理张亮 为了支持定时器,Libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数.时间缓存.时间校正和定时器堆的时间值调整等.下面就结合源代码来分析一下. 1 初始化检测 Libevent在初始化时会检测系统时间的类型,通过调用函数detect_monotonic()完成,它通过调用clock_gettime()来检测系统是否支持monotonic时钟类型: [cpp] view plaincopy static v

libevent源码深度剖析七

libevent源码深度剖析七 --事件主循环张亮 现在我们已经初步了解了libevent的Reactor组件--event_base和事件管理框架,接下来就是libevent事件处理的中心部分 --事件主循环,根据系统提供的事件多路分发机制执行事件循环,对已注册的就绪事件,调用注册事件的回调函数来处理事件. 1 阶段性的胜利 Libevent将I/O事件.定时器和信号事件处理很好的结合到了一起,本节也会介绍libevent是如何做到这一点的.     在看完本节的内容后,读者应该会对Libev

libevent源码深度剖析八

libevent源码深度剖析八 --集成信号处理张亮 现在我们已经了解了libevent的基本框架:事件管理框架和事件主循环.上节提到了libevent中I/O事件和Signal以及Timer事件的集成,这一节将分析如何将Signal集成到事件主循环的框架中. 1 集成策略--使用socket pair 前一节已经做了足够多的介绍了,基本方法就是采用"消息机制".在libevent中这是通过socket pair完成的,下面就来详细分析一下.      Socket pair就是一个s