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

hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之《Linux环境下的事件驱动力量:探索Libevent的高性能I/O架构》,在这篇文章中,你将会学习到Libevent的高性能I/O原理以及应用,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!

希望这篇文章能对你有所帮助9fe07955741149f3aabeb4f503cab15a.png,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!1a2b6b564fe64bee9090c1ca15a449e3.png(注:这章对于高性能服务器的架构非常重要哟!!!)

03d6d5d7168e4ccb946ff0532d6eb8b9.gif         

 

 

目录

 一.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方法来删除事件多路分发器中的一个事件。

 

 对应的框架组件图如下:

 

 

二 .Libevent工作流程

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

  1. 初始化

    • 调用 event_base_new()(或旧版本的 event_init())来创建和初始化一个 event_base 实例,这是 Libevent 的核心结构,它代表了一个事件循环。
  2. 创建事件

    • 使用 event_new() 或 event_assign() 创建一个 event 实例,并设置其文件描述符、事件类型(如 EV_READ、EV_WRITE)、事件回调函数以及用户数据。
  3. 添加事件

    • 调用 event_add() 将事件添加到 event_base 中,可以指定一个超时时间,这样事件会在指定的时间后被触发。
  4. 事件循环

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

    • 当事件准备好时,Libevent 会调用与之关联的回调函数。
    • 回调函数执行完毕后,Libevent 会继续监听其他事件。
  6. 修改或删除事件

    • 可以通过 event_del() 从 event_base 中删除事件,或者通过 event_add() 修改事件的超时时间或重新添加事件。
  7. 清理

    • 当不再需要事件循环时,可以调用 event_base_free() 来释放 event_base 实例,这将清理所有关联的资源。

在整个工作流程中,Libevent 提供了高效的事件管理机制,使得开发者可以专注于事件的处理,而无需关心底层的IO多路复用细节。此外,Libevent 还提供了缓冲事件(bufferevent)等高级接口,进一步简化了非阻塞IO的处理。

对应工作流程图:

 

三 . 一个简单实例展示流程

  

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源代码组织结构

Libevent的源代码组织结构可以按照其提供的功能和特性进行分类。以下是根据源代码目录和功能进行的分类总结:

  1. 核心事件循环

    • event.c:实现事件循环的核心逻辑。
    • evmap.c:管理事件和文件描述符之间的映射。
    • minheap.c:提供最小堆数据结构,用于定时器管理。
  2. 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支持。
  3. 网络通信

    • http.c:HTTP服务器和客户端的实现。
    • http_server.c:HTTP服务器的实现。
    • http_header.c:HTTP头的解析。
    • http_parser.c:HTTP请求/响应的解析。
    • evdns.c:DNS解析器。
  4. 缓冲区和数据管理

    • buffer.c:基础缓冲区管理。
    • evbuffer.c:扩展的缓冲区管理。
    • bufferevent.c:缓冲事件,用于非阻塞IO。
    • bufferevent_async.c:缓冲事件异步支持。
    • bufferevent_filter.c:缓冲事件过滤器。
    • bufferevent_pair.c:缓冲事件对。
    • bufferevent_ratelim.c:缓冲事件速率限制。
  5. 线程和锁

    • evthread.c:线程支持。
    • event_tagging.c:事件标签支持,用于无锁编程。
  6. 信号处理

    • evsignal.c:信号处理。
    • signal.c:信号处理。
  7. 实用工具和辅助功能

    • evutil.c:实用工具函数。
    • arc4random.c:随机数生成器。
    • strlcpy.c:字符串操作。
    • sys_socket.c:系统socket支持。
    • sys_event.c:系统事件支持。
  8. 定时器

    • timer.c:定时器实现。
    • wristwatch.c:高精度定时器支持。
  9. 其他

    • evrpc.c:RPC客户端/服务器实现。
    • htmlevents.c:HTML解析器。

这个分类总结展示了Libevent的模块化设计,每个模块负责一个特定的功能,使得Libevent易于扩展和维护。开发者可以根据需要选择和使用不同的模块来构建网络应用程序。

 对于I/O库,我们还需要进行优化:

Libevent 是一个高性能的事件通知库,但它也提供了多种选项和策略来帮助开发者进行性能调优。以下是一些可以用来优化 Libevent 性能的策略:

  1. 使用合适的 IO 多路复用机制

    • 在 Linux 上,如果可能的话,使用 epoll 而不是 select 或 pollepoll 通常提供更高的性能,尤其是在处理大量文件描述符时。
    • 在支持 kqueue 的系统上(如 macOS 和 FreeBSD),使用 kqueue 可以提供更好的性能。
  2. 使用边缘触发 (ET) 模式

    • 默认情况下,Libevent 使用水平触发 (LT) 模式。如果你熟悉 ET 模式的工作方式,可以切换到 ET 模式,这可能会提供更高的性能,但需要更仔细地处理事件。
  3. 优化事件处理函数

    • 确保 IO 事件的处理函数尽可能高效。避免在事件处理函数中进行阻塞操作或执行耗时较长的任务。
    • 如果需要在事件处理函数中执行耗时操作,考虑使用线程池或异步操作。
  4. 减少锁的使用

    • 在多线程环境中,减少对共享资源的锁定时间可以提高性能。只在必要时使用锁,并尽量减少锁的粒度。
  5. 使用缓冲事件 (bufferevent)

    • 使用 bufferevent 可以简化非阻塞 IO 的处理,因为它会自动处理数据的读取和写入,以及相关的 IO 事件。
    • bufferevent 可以减少对事件循环的干扰,因为它会在内部缓冲数据,直到有足够的数据可以处理或发送。
  6. 优化定时器使用

    • 如果你的应用程序使用了大量的定时器,确保它们被高效地管理和使用。避免不必要的定时器添加和删除操作。
  7. 调整事件通知库的配置参数

    • Libevent 允许你调整内部参数,如事件队列大小、超时时间等。根据应用程序的需求调整这些参数可能会提高性能。
  8. 使用最新版本的 Libevent

    • 保持 Libevent 库的更新可以确保你获得最新的性能改进和 bug 修复。
  9. 监控和性能分析

    • 使用性能分析工具来监控你的应用程序,找出瓶颈所在。这可以帮助你确定哪些部分最需要优化。
  10. 避免不必要的内存分配

    • 减少 malloc 和 free 的调用次数,尽量重用内存。Libevent 提供了内存池功能,可以用来减少内存分配的开销。

通过这些策略,你可以优化使用 Libevent 的应用程序的性能。然而,性能调优通常需要根据具体的应用程序和运行环境来进行,因此最好结合具体情况进行调整。

 

最后,我给出GitHub中的源码仓库链接:https://github.com/libevent/libevent,大家需要看源码的可以自己下载学习哟!!!

      好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦4d7d9707063b4d9c90ac2bca034b5705.png!! 感谢大家的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!2cd0d6ee4ef84605933ed7c04d71cfef.jpeg  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/7221.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一竞技MSI:淘汰赛抽签结果出炉 BLG和T1同半区,TES首轮交手TL!

北京时间5月6日,MSI在今天进入短暂的休赛,在昨天结束的入围赛之后,PSG战队作为外卡赛区唯一的队伍进入到正赛,另外欧洲赛区的FNC战队也是击败GAM战队拿到正赛的资格。在比赛结束之后,也是进行了淘汰赛的胜败分组赛的抽…

Llama3-Tutorial之LMDeploy高效部署Llama3实践

Llama3-Tutorial之LMDeploy高效部署Llama3实践 Llama 3 近期重磅发布,发布了 8B 和 70B 参数量的模型,lmdeploy团队对 Llama 3 部署进行了光速支持!!! 书生浦语和机智流社区同学光速投稿了 LMDeploy 高效量化部署 Llam…

OpenHarmony实战开发-上传文件

Web组件支持前端页面选择文件上传功能,应用开发者可以使用onShowFileSelector()接口来处理前端页面文件上传的请求。 下面的示例中,当用户在前端页面点击文件上传按钮,应用侧在onShowFileSelector()接口中收到文件上传请求,在此接…

详解xml-java语言

1.XML在线学习手册 XML 教程 2.XML可以做什么 1.给两个程序之间进行数据通信。现在用的最多的是JSON。 2.给服务器做配置文件。 3.存储复杂的数据关系。 4.还可以充当小型的数据库。 3.书写格式 <?xml version"1.0" encoding"UTF-8" ?> <…

使用Gitbook生成电子书

背景 《Google工程实践文档》相对原文Google’s Engineering Practices documentation &#xff0c;部分内容过时了。需要更新中文版&#xff0c;并使用Gitbook把Markdown文件转换成对应的PDF电子书。   上一次生成PDF电子书是5年前&#xff0c;当时生成电子书的环境早已不在…

虚拟键代码

虚拟键代码 虚拟键码 (Winuser.h) - Win32 apps | Microsoft Learn 在Windows操作系统中&#xff0c;虚拟键代码&#xff08;Virtual-Key Codes&#xff09;是一组用来表示键盘上按键的数值。这些代码通常用于Windows API函数&#xff0c;以便程序能够识别和处理键盘输入。 虚拟…

Linux第三节--常见的指令介绍集合(持续更新中)

点赞关注不迷路&#xff01;&#xff0c;本节涉及初识Linux第三节&#xff0c;主要为常见的几条指令介绍。 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1f44d;&#x1f3fb; 收藏 ✨ 加关注&#x1f440; 期待与你共同进步! Linux下基本指令 1. man指令 Linu…

百科词条创建机构有哪些?

在互联网时代&#xff0c;百度百科作为我国最大的中文百科全书&#xff0c;已经成为人们获取知识、查询信息的重要途径。随着百度百科影响力的不断扩大&#xff0c;越来越多的人和企业试图通过创建企业词条来提升自身知名度&#xff0c;企业和个人为了在百度百科上占据一席之地…

GoLand安装教程

GoLand-安装 GoLand是Go语言编程开发的一款工具&#xff0c;和 IntelliJ IDEA 一样&#xff0c;同为Jetbrains公司旗下的产品&#xff0c;专为Go语言开发的跨平台商业集成开发环境&#xff08;IDE&#xff09;&#xff0c;它的功能非常强大&#xff0c;它还不仅仅是一个Go IDE…

记一次动态规划的采坑之旅, 741摘樱桃 https://leetcode.cn/problems/cherry-pickup/description/

首次看题目时&#xff0c;发现是困难。立马想到了&#xff0c;动态规划。 再看题目&#xff0c; 摘樱桃&#xff0c;还要返回摘两次&#xff0c;求摘最多的樱桃。 大脑第一反应就是&#xff1a; 先使用动态规划&#xff0c;找到 0 0 到 n-1 n-1处走过的最大樱桃&#xff0c; 并…

重写muduo之EPollPoller

1、EPollPoller.h EPollPoller的主要实现&#xff1a;作为poller的派生类&#xff0c;把基类给派生类保留的这些纯虚函数的接口实现出来。 override表示在派生类里面&#xff0c;这些方法是覆盖方法。必须由编译器来保证在基类里面一定有这些函数的接口的声明。在派生类要重写…

从OutputStream类看Java中的IO流操作

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

C#连接S7-200 smart通讯测试

honeytree 一、编程环境 VS2022软件&#xff0c;选择windows窗体应用&#xff08;.NET FrameWork&#xff09;&#xff1a;​博途TIA/WINCC社区VX群 ​博途TIA/WINCC社区VX群 添加NuGet程序包&#xff1b;S7netplus 二、引用http://S7.net 三、建立PLC链接 S7-200smart和…

使用Docker安装Jenkins

大家好&#xff0c;今天给大家分享如何使用docker安装jenkins&#xff0c;关于docker的安装和常用命令可以参考下面两篇文章&#xff0c;使用docker可以提高资源利用率&#xff0c;能够在不同的环境中轻松迁移和部署应用&#xff0c;在本文中就不过多赘述了。 Docker常用命令 …

工厂模式+策略模式完成多种登录模式的实现

前提 &#xff08;简单工厂不属于设计模式&#xff0c;而是一种编程思想【抽象一层出来】&#xff09;工厂方法模式、抽象工厂模式 以上都是为了解耦&#xff0c;如果考虑多个纬度&#xff08;如需要同时考虑多种电器&#xff0c;多种品牌&#xff09;则优先考虑抽象工厂。 …

怎么通过Java语言实现远程控制无人售货柜

怎么通过Java语言实现远程控制无人售货柜呢&#xff1f; 本文描述了使用Java语言调用HTTP接口&#xff0c;实现控制无人售货柜&#xff0c;独立控制售货柜、格子柜的柜门。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂商1智能WiFi控…

ASP.NET网上图书预约系统的设计

摘 要 《网上图书预约系统的设计》是以为读者提供便利为前提而开发的一个信息管理系统&#xff0c;它不仅要求建立数据的一致性和完整性&#xff0c;而且还需要应用程序功能的完备、易用等特点。系统主要采用VB.NET作为前端的应用开发工具&#xff0c;利用SQL Server2000数据…

7-35 有理数均值

题目链接&#xff1a;7-35 有理数均值 一. 题目 1. 题目 2. 输入输出样例 3. 限制 二、代码 1. 代码实现 #include <iostream> using namespace std;// 计算公约数 int calGcd(int a, int b) {int gcd;bool negative false;if (a a / b * b) { // b整除areturn b;}…

Llama3-Tutorial之Llama3本地Web Demo部署

Llama3-Tutorial之Llama3本地 Web Demo部署 Llama3-Tutorial之Llama3本地Web Demo部署章节。 参考&#xff1a; https://github.com/SmartFlowAI/Llama3-Tutorial 1. 环境配置 conda create -n llama3 python3.10conda activate llama3conda install pytorch2.1.2 torchvision0…

【RAG 论文】SKR:Self-Knowledge 指导下的 RAG

论文&#xff1a;Self-Knowledge Guided Retrieval Augmentation for Large Language Models ⭐⭐⭐⭐ Tsinghua, arXiv:2310.05002 文章目录 一、论文速读二、实现细节2.1 数据的收集2.2 引出 LLM 的 Self-Knowledge 的方法1&#xff09;Direct Prompting2&#xff09;In-Cont…