第12章 高性能I/O框架库Libevent

        Libevent是开源社区一款高性能的I/O框架库,具有如下特点:

  • 跨平台支持。Libevent支持Linux、Unix、Windows
  • 统一事件源。Libevent对I/O事件、信号、定时事件提供统一的处理。
  • 线程安全。Libevent使用libevent_pthreads库提供线程安全支持。
  • 基于Reactor模式实现。

12.1 I/O框架库概述

        各种I/O框架库的实现原理基本相似,要么以Reactor模式实现,要么以Proactor模式实现,要么同时以这两种模式实现。

以Reactor模式为例,Reactor模式的I/O框架包含如下几个组件:

  • 句柄(Handle)
  • 事件多路分发器(EventDemultiplexer)
  • 事件处理器(EventHandler)
  • 具体事件处理器(ConcreteEventHandler)
  • Reactor

这些组件的关系如下图所示:

(1)句柄

        句柄的作用是,当内核检测到就绪事件时,他将通过句柄来通知应用程序这一事件。在Linux环境下,I/O事件对应的句柄时文件描述符,信号对应的句柄是信号值。

(2)事件多路分发器

        事件的到来是随机的、异步的,所以程序要循环等待并处理事件。在循环中,等待事件一般使用I/O复用技术来实现。I/O框架库将各种I/O复用系统调用封装成统一的接口,称为事件多路分发器事件分发器的demultiplex方法是等待事件的核心函数,其内部调用的是select、poll、epoll_wait等函数。此外,事件分发器还需要实现register_event和remove_event方法,以供调用者往事件分发器中添加事件和删除事件

(3)事件处理器和具体事件处理器

        事件处理器执行事件对应的业务逻辑。它通常包含一个或多个handle_event回调函数,这些回调函数在事件循环中被执行。事件处理器一般还提供一个get_handle方法,它返回与该事件处理器关联的句柄。当事件分发器检测到有事件发生时,会通过句柄通知应用程序。因此,必须将事件处理器和句柄绑定

        事件处理器通常是一个接口,用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此事件处理器中的回调函数一般是虚函数。

(4)Reactor

        Reactor时I/O框架库的核心。它提供一下几个主要方法:

  • handle_events:执行事件循环,即:循环等待事件,然后一次处理所有就绪事件对应的事件处理器。
  • register_handler:调用事件分发器的register_event方法来向事件分发器中注册一个事件。
  • remove_handler:调用事件分发器的remove_event方法来删除事件分发器中的一个事件。

        下图总结了I/O框架库的工作时序:

12.2 Libevent源码分析

12.2.1 一个实例

        使用Libevent库实现一个“Hello World”程序。

#include <sys/signal.h>
#include <event.h>

void signal_cb(int fd, short event, void* argc)
{
    struct event_base* base = (event_base*)argc;
    struct timeval delay = {2, 0};
    printf("caught an interrupt signal, exiting in two seconds\n");
    event_base_loopexit(base, &delay);
}

void timeout_cb(int fd, short event, void* argc)
{
    printf("timeout\n");
}

int main()
{
    struct event_base* base = event_init();

    struct event* signal_event = evsignal_new(base, SIGINT, signal_cb, base);
    event_add(signal_event, NULL);

    timeval tv = {1, 0};
    struct event* timeout_event = evtimer_new(base, timeout_cb, NULL);
    event_add(timeout_event, &tv);

    event_base_dispatch(base);

    event_free(timeout_event);
    event_free(signal_event);
    event_base_free(base);
    return 0;
}

        上面代码描述出了Libevent库的主要逻辑:

(1)使用event_init函数创建event_base对象。一个event_base相当于一个Reactor实例。

(2)创建具体的事件处理器,并设置它们所从属的Reactor实例。evsignal_new和evtimer_new分别用于创建信号事件处理器和定时事件处理器,它们时event.h文件中的宏:

#define evsignal_new(b,x,cb,arg) event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
#define evtimer_new(b,cb,arg) event_new((b), -1, 0, (cb), (arg))

可见,它们的统一入口是event_new函数,即:用于创建通用事件处理器(EventHandler)的函数,其定义是:

struct event *
event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg);
参数解释:
base:指定新创建的事件处理器从属的Reactor
fd:指定与该事件处理器关联的句柄。
(创建I/O事件处理器时,应该给fd参数传递文件描述符值;创建信号事件处理器时,应该给fd参数传递信号值;创建定时器事件时,应该给fd参数传递-1)
events:指定事件类型,可选值如下:
    #define    EV_TIMEOUT    0x01    //定时事件
    #define    EV_READ       0x02    //可读事件
    #define    EV_WRITE      0x04    //可写事件
    #define    EV_SIGNAL     0x08    //信号事件
    #define    EV_PERSIST    0x10    //永久事件
    #define    EV_ET         0x20    //边缘触发事件
cb:指定事件对应的回调函数。相当于事件处理器中的handle_event方法。
arg:Reactor传递给回调函数的参数
返回值:
成功时返回一个event类型的对象,也就是Libevent的事件处理器。
注:libevent用event来描述事件处理器,而不是事件。

        为了区分事件和事件处理器,做如下约定:

  • 事件指一个句柄上绑定的事件。如文件描述符上的可读事件
  • 事件处理器,即event结构体对象,包含句柄和事件类型,及回调函数等。
  • 事件由事件多路分发器管理;事件处理器由事件队列管理。
  • 事件循环对一个就绪事件的处理,指的是执行该事件对应的事件处理器中的回调函数。

(3)使用event_add函数将事件处理器添加到注册事件队列中,并将该事件处理器对应的事件添加到事件分发器中。event_add相当鱼Reactor中的register_handler函数。

(4)使用event_base_dispatch函数执行事件循环。

(5)事件循环结束后,使用*_free系列函数释放系统资源。

11.2.2 源代码组织结构

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值