立山 发表于 2024-6-14 22:45:11

Linux环境下的事件驱动力量:探索Libevent的高性能I/O架构

hello !各人好呀! 接待各人来到我的Linux高性能服务器编程系列之《Linux环境下的事件驱动力量:探索Libevent的高性能I/O架构》,在这篇文章中,你将会学习到Libevent的高性能I/O原理以及应用,并且我会给出源码进行分析,以及手绘UML图来帮助各人来明白,希望能让各人更能相识网络编程技能!!!
希望这篇文章能对你有所帮助https://img-blog.csdnimg.cn/direct/9fe07955741149f3aabeb4f503cab15a.png,各人要是觉得我写的不错的话,那就点点免费的小爱心吧!https://img-blog.csdnimg.cn/direct/1a2b6b564fe64bee9090c1ca15a449e3.png(注:这章对于高性能服务器的架构非常告急哟!!!)
https://img-blog.csdnimg.cn/direct/03d6d5d7168e4ccb946ff0532d6eb8b9.gif         
 https://img-blog.csdnimg.cn/direct/480a4f29a0e34f0cb54d57e55f816eff.jpeg
 
目录
 一.Libevent简介
二 .Libevent工作流程
三 . 一个简朴实例展示流程
四 . Libevent源代码组织布局

 一.Libevent简介

     I/O 框架库以库函数的形式,封装了较为底层的系统调用,给应用步伐提供了一组更便于使用的接口。这些库函数通常比步伐员本身实现的同样功能的函数更公道、更高效,且更健壮。因为它们经受住了真实网络环境下的高压测试,以实时间的检验。
  各种I/O框架库的实现原理根本相似,要么以Reactor模式实现, 要么以 Proactor模式实现,要么同时以这两种模式实现。举例来说,基于Reactor模式的I/O框架库包罗如下几个组件: 句柄(Handle)、事件多路分发器(EventDemultiplexer)、事件处置处罚器(EventHandler)和具体的事件处置处罚器(ConcreteEventHandler)。
       1.句柄 :I/O框架库要处置处罚的对象,即I/O 事件、信号和定时事件,统一称为事件源。一个事件源通常和一个句柄绑定在一起。句柄的作用是,当内核检测到就绪事件时,它将通过句柄来通知应用步伐这一事件。在Linux环境下,I/O 事件对应的句柄是文件描述符,信号事件对应的句柄就是信号值。

    2.事件多路分发器 :事件的到来是随机的、异步的。我们无法预知步伐何时收到一个客户连接请求,又亦或收到一个停息信号。所以步伐须要循环地等待并处置处罚事件,这就是事件循环。在事件循环中,等待事件一样平常使用I/O 复用技能来实现。I/O 框架库一样平常将系统支持的各种I/O 复用系统调用封装成统一的接口,称为事件多路分发器。事件多路分发器的demultiplex 方法是等待事件的核心函数,其内部调用的是 select、poll、epoll _ wait等函数。
 
    3.事件处置处罚器和具体事件处置处罚器 : 事件处置处罚器执行事件对应的业务逻辑。它通常包罗一个或多个hand le event回调函数,这些回调函数在事件循环中被执行。I/O框架库提供的事件处置处罚器通常是一个接口,用户须要继承它来实现本身的事件处置处罚器,即具体事件处置处罚器。因此,事件处置处罚器中的回调函数一样平常被声明为虚函数,以支持用户的扩展。别的,事件处置处罚器一样平常还提供一个get handle方法,它返回与该事件处置处罚器关联的句柄。那么,事件处置处罚器和句柄有什么关系?当事件多路分发器检测到有事件发生时,它是通过句柄来通知应用步伐的。因此,我们必须将事件处置处罚器和句柄绑定,才气在事件发生时获取到正确的事件处置处罚器。

    4. Reactor是I/O 框架库的核心。它提供的几个告急方法是:handle _ events。该方法执行事件循环。它重复如下过程:等待事件,然后依次处置处罚全部就绪事件对应的事件处置处罚器。  register _ handler。该方法调用事件多路分发器的register _ event方法来往事件多路分发器中注册一个事件。remove _ handler。该方法调用事件多路分发器的remove _ event方法来删除事件多路分发器中的一个事件。
 
 对应的框架组件图如下:
https://img-blog.csdnimg.cn/direct/6bd13c4867014e619147c467861f2b0d.jpeg
 
 
二 .Libevent工作流程

   Libevent 的工作流程可以概括为以下几个步调:

[*] 初始化:

[*]调用 event_base_new()(或旧版本的 event_init())来创建和初始化一个 event_base 实例,这是 Libevent 的核心布局,它代表了一个事件循环。

[*] 创建事件:

[*]使用 event_new() 或 event_assign() 创建一个 event 实例,并设置其文件描述符、事件范例(如 EV_READ、EV_WRITE)、事件回调函数以及用户数据。

[*] 添加事件:

[*]调用 event_add() 将事件添加到 event_base 中,可以指定一个超时时间,如许事件会在指定的时间后被触发。

[*] 事件循环:

[*]调用 event_base_dispatch() 开始事件循环。这个函数会阻塞,直到至少有一个事件预备好。
[*]在内部,Libevent 会根据操纵系统的支持选择符合的事件多路复用机制(如 epoll、select、kqueue)来等待事件。

[*] 事件处置处罚:

[*]当事件预备好时,Libevent 会调用与之关联的回调函数。
[*]回调函数执行完毕后,Libevent 会继续监听其他事件。

[*] 修改或删除事件:

[*]可以通过 event_del() 从 event_base 中删除事件,或者通过 event_add() 修改事件的超时时间或重新添加事件。

[*] 清算:

[*]当不再须要事件循环时,可以调用 event_base_free() 来开释 event_base 实例,这将清算全部关联的资源。

在整个工作流程中,Libevent 提供了高效的事件管理机制,使得开发者可以专注于事件的处置处罚,而无需关心底层的IO多路复用细节。别的,Libevent 还提供了缓冲事件(bufferevent)等高级接口,进一步简化了非阻塞IO的处置处罚。
对应工作流程图:
https://img-blog.csdnimg.cn/direct/6337e5891cd44151b5d765c0fdb50338.jpeg
 
三 . 一个简朴实例展示流程

  
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 _ looperit( 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 = ovtimer 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 );
}
    代码清单12-1虽然简朴,但却根本上描述了Libevent库的告急逻辑:
   1) 调用event _ init函数创建event _ base 对象。一个event _ base相称于一个Reactor实例。

   2)创建具体的事件处置处罚器, 并设置它们所从属的Reactor实例。evsignal _ new和evtimer _ new 分别用于创建信号事件处置处罚器和定时事件处置处罚器,它们是定义在include/event2/event. h文件中的宏:
#define evsignal _ new(b, x, cb, arg)event _ new((b), (x), EV_SIGNAL|EV_PERSIST, (2b) (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参数传递信号值,好比代码清单中的 SIGINT;创建定时事件处置处罚器时,则应该传入fd参数-1 , events参数指定事件范例,其可选值如下:
#define EV_TIMEOUT  0x01  /*定时事件 */
#define EV_READ  0x02  /*可读事件 */
#define EV_WRITE  0x04  /*可写事件 */
#define EV_SIGNAL  0x08  /*信号事件 */
#define EV_PERSIST  0×10  /*永久事件 */
/*边沿触发事件,需要I/O复用系统调用支持,比如 epoll */
#define EV_ET  0x20

四 . Libevent源代码组织布局

https://img-blog.csdnimg.cn/direct/49c151617092415792ef03e2e925f418.png
Libevent的源代码组织布局可以按照其提供的功能和特性进行分类。以下是根据源代码目录和功能进行的分类总结:

[*] 核心事件循环:

[*]event.c:实现事件循环的核心逻辑。
[*]evmap.c:管理事件和文件描述符之间的映射。
[*]minheap.c:提供最小堆数据布局,用于定时器管理。

[*] IO多路复用机制:

[*]epoll.c:Linux上的epoll支持。
[*]select.c:传统的select支持。
[*]poll.c:poll支持。
[*]kqueue.c:BSD系统上的kqueue支持。
[*]devpoll.c:Solaris上的/dev/poll支持。
[*]evport.c:Solaris事件端口支持。
[*]iocp.c:Windows上的IOCP支持。

[*] 网络通信:

[*]http.c:HTTP服务器和客户端的实现。
[*]http_server.c:HTTP服务器的实现。
[*]http_header.c:HTTP头的分析。
[*]http_parser.c:HTTP请求/相应的分析。
[*]evdns.c:DNS分析器。

[*] 缓冲区和数据管理:

[*]buffer.c:基础缓冲区管理。
[*]evbuffer.c:扩展的缓冲区管理。
[*]bufferevent.c:缓冲事件,用于非阻塞IO。
[*]bufferevent_async.c:缓冲事件异步支持。
[*]bufferevent_filter.c:缓冲事件过滤器。
[*]bufferevent_pair.c:缓冲事件对。
[*]bufferevent_ratelim.c:缓冲事件速率限定。

[*] 线程和锁:

[*]evthread.c:线程支持。
[*]event_tagging.c:事件标签支持,用于无锁编程。

[*] 信号处置处罚:

[*]evsignal.c:信号处置处罚。
[*]signal.c:信号处置处罚。

[*] 实用工具和辅助功能:

[*]evutil.c:实用工具函数。
[*]arc4random.c:随机数天生器。
[*]strlcpy.c:字符串操纵。
[*]sys_socket.c:系统socket支持。
[*]sys_event.c:系统事件支持。

[*] 定时器:

[*]timer.c:定时器实现。
[*]wristwatch.c:高精度定时器支持。

[*] 其他:

[*]evrpc.c:RPC客户端/服务器实现。
[*]htmlevents.c:HTML分析器。

这个分类总结展示了Libevent的模块化设计,每个模块负责一个特定的功能,使得Libevent易于扩展和维护。开发者可以根据须要选择和使用不同的模块来构建网络应用步伐。
 对于I/O库,我们还须要进行优化:
Libevent 是一个高性能的事件通知库,但它也提供了多种选项和战略来帮助开发者进行性能调优。以下是一些可以用来优化 Libevent 性能的战略:
   
[*] 使用符合的 IO 多路复用机制:

[*]在 Linux 上,假如可能的话,使用 epoll 而不是 select 或 poll。epoll 通常提供更高的性能,尤其是在处置处罚大量文件描述符时。
[*]在支持 kqueue 的系统上(如 macOS 和 FreeBSD),使用 kqueue 可以提供更好的性能。

[*] 使用边缘触发 (ET) 模式:

[*]默认情况下,Libevent 使用水平触发 (LT) 模式。假如你熟悉 ET 模式的工作方式,可以切换到 ET 模式,这可能会提供更高的性能,但须要更仔细地处置处罚事件。

[*] 优化事件处置处罚函数:

[*]确保 IO 事件的处置处罚函数尽可能高效。制止在事件处置处罚函数中进行阻塞操纵或执行耗时较长的任务。
[*]假如须要在事件处置处罚函数中执行耗时操纵,思量使用线程池或异步操纵。

[*] 淘汰锁的使用:

[*]在多线程环境中,淘汰对共享资源的锁定时间可以提高性能。只在须要时使用锁,并只管淘汰锁的粒度。

[*] 使用缓冲事件 (bufferevent):

[*]使用 bufferevent 可以简化非阻塞 IO 的处置处罚,因为它会主动处置处罚数据的读取和写入,以及相关的 IO 事件。
[*]bufferevent 可以淘汰对事件循环的干扰,因为它会在内部缓冲数据,直到有充足的数据可以处置处罚或发送。

[*] 优化定时器使用:

[*]假如你的应用步伐使用了大量的定时器,确保它们被高效地管理和使用。制止不须要的定时器添加和删除操纵。

[*] 调解事件通知库的配置参数:

[*]Libevent 允许你调解内部参数,如事件队列大小、超时时间等。根据应用步伐的需求调解这些参数可能会提高性能。

[*] 使用最新版本的 Libevent:

[*]保持 Libevent 库的更新可以确保你获得最新的性能改进和 bug 修复。

[*] 监控和性能分析:

[*]使用性能分析工具来监控你的应用步伐,找出瓶颈地点。这可以帮助你确定哪些部分最须要优化。

[*] 制止不须要的内存分配:

[*]淘汰 malloc 和 free 的调用次数,只管重用内存。Libevent 提供了内存池功能,可以用来淘汰内存分配的开销。

通过这些战略,你可以优化使用 Libevent 的应用步伐的性能。然而,性能调优通常须要根据具体的应用步伐和运行环境来进行,因此最好结合具体情况进行调解。
 
末了,我给出GitHub中的源码仓库链接:https://github.com/libevent/libevent,各人须要看源码的可以本身下载学习哟!!!

         好啦!到这里这篇文章就结束啦,关于实例代码中我写了许多注释,假如各人另有不懂得,可以批评区或者私信我都可以哦https://img-blog.csdnimg.cn/direct/4d7d9707063b4d9c90ac2bca034b5705.png!! 感谢各人的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!https://img-blog.csdnimg.cn/direct/2cd0d6ee4ef84605933ed7c04d71cfef.jpeg  

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Linux环境下的事件驱动力量:探索Libevent的高性能I/O架构