Linux高性能I/O框架库Libevent怎么用

发布时间:2022-02-24 16:05:50 作者:iii
来源:亿速云 阅读:129

本文小编为大家详细介绍“Linux高性能I/O框架库Libevent怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Linux高性能I/O框架库Libevent怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

Linux服务器程序必须处理的三类事件:

在处理这三类事件时我们通常需要考虑如下三个问题:

幸运的是,开源社区提供了很多优秀的I/O框架库,他们不仅解决了上述问题,让开发者可以将精力完全放在程序的逻辑上,而且稳定性、性能等各方面都相当出色。而Libevent就是其中相对轻量级的框架库。

I/O框架库概述

I/O框架库以库函数的形式,封装了较为底层的系统调用,给应用程序提供了一组更便于使用的接口。这些库函数往往比程序员自己实现的同样功能的函数更合理、更高效、且更健壮。因为它们经受住了真实网络环境下的高压测试,以及时间的考验。

各种I/O框架库的实现原理基本相似,要么以Reactor模式实现,要么以Procator模式实现(高性能服务器程序框架 - 两种高效的事件处理模式),要么同时以这两种模式实现。举例来说,基于Reactor模式的I/O框架库包含如下几个组件:

  1. 句柄: I/O框架库要处理的对象,即I/O事件、信号和定时事件,统一称为事件源。一个事件源通常和一个句柄绑定在一起。句柄的作用是,当内核检测到就绪事件时,它将通过句柄来通知应用程序这一事件。在Linux环境下,I/O事件对应的句柄是文件描述符,信号事件对应的句柄就是信号值。

  2. 事件多路分发器:事件的到来是随机的、异步的。我们无法预知程序何时收到一个客户连接请求,又亦活收到一个暂停信号。所以程序需要循环地等待并处理事件,这就是事件循环。在事件循环中,等待事件一般使用I/O复用技术来实现。I/O框架库一般将系统支持的各种I/O复用系统调用封装成统一的接口,称为事件多路分发器。事件多路分发器的demultiplex方法是等待事件的核心函数,其内部调用的是selectpollepoll_wait等函数。此外事件多路分发器还需实现register_eventremove_event方法,以供调用者往事件多路分发器中添加事件和从事件多路分发器中删除事件。

  3. 事件处理器和具体时间处理器:事件处理器执行事件对应的业务逻辑。它通常包含一个或多个handle_event回调函数,这些回调函数在事件循环中被执行。I/O框架库提供的事件处理器通常是一个接口,用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此,事件处理器中的回调函数一般被声明为需函数,以支持用户的扩展。此外,事件处理器一般还提供一个get_handle方法,它返回与该事件处理器关联的句柄。那么事件处理器和句柄有什么关系?当时间多路分发器检测到有事件发生时,它是通过句柄来通知应用程序的。因此,我们必须将事件处理器和句柄绑定,才能在事件发生时获取到正确的事件处理器。

  4. Reactor:Reactor是I/O框架的核心。它提供的几个主要方法是:

Libevent源码分析

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

(推荐微课:Linux微课)

一个实例

下面是用Libevent库实现的一个“Hello World”程序。

include <sys/signal.h>

#include <event2/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 cleanly in two seconds....\n");
    event_base_loopexit(base, &delay);
}


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


int main(int argc, char const *argv[])
{
    struct event_base* base = event_base_new();
    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_base_new函数创建event_base对象。一个event_base相当于一个Reactor实例。

  2. 创建具体的事件处理器,并设置它们所从属的Reactor实例。evsignal_newevtimer_new分别用于创建信号事务处理器和定时事件处理器。

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函数,即用于创建通用事件处理器的函数,定义如下:

event_new(struct event_base base, evutil_socket_t fd, short events, void (cb)(evutil_socket_t, short, void ), void arg)其中,base参数指定行

其中:

    #define EV_TIMEOUT  0x01   /*定时事件*/
    #define EV_READ     0x02         /*可读事件*/
    #define EV_WRITE    0x04        /*可写事件*/
    #define EV_SIGNAL   0x08       /*信号事件*/
    #define EV_PERSIST  0x10     /*永久事件*/
    /*边缘触发事件,需要I/O复用系统调用支持,比如epoll */
    #define EV_ET       0x20

上述代码中,EV_PERSIST的作用是:事件被触发后,自动重新对这个event调用event_add函数。

event_new函数成功时返回一个event类型的对象,也就是Libevent的事件处理器。Libevent用单词“event”来描述事件处理器,而不是事件,所以约定如下:

  1. 调用event_add函数,将事件处理器添加到注册事件队列中,并将该事件处理器对应的事件添加到事件多路分发器中。even_add函数相当于Reactor中的register_handler方法。

  2. 调用event_base_dispatch函数来执行事件循环

  3. 事件循环结束后,使用*_free系列释放系统资源

(推荐课程:Linux就该这么学)

源代码组织结构

在整个源码中,event-internal.hinclude/event2/event_struct.hevent.cevmap.c等4个文件最为重要。它们定义了eventevent_base结构体,并实现了这两个结构体的相关操作。

读到这里,这篇“Linux高性能I/O框架库Libevent怎么用”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

推荐阅读:
  1. ubuntu编译libevent遇到的问题
  2. swoole与libevent的区别是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

libevent linux i/o

上一篇:Linux内核实时系统有哪些知识点

下一篇:常用的Linux命令是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》