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

news2024/11/12 22:27:41

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.coloradmin.cn/o/1646011.html

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

相关文章

攻防世界-xff-referer

题目信息 分析过程 显示ip必须为123.123.123.123,则进行伪造 解题过程 打开repeator 提示必须来自https://www.google.com,则再次构造Referer 相关知识 x-forwarded-for 和 referer的区别: x-forwarded-for 用来证明ip的像是“127.0.0.1”这种&a…

为什么感觉没有效果

以前在辅导小儿作业的时候,我会在常用的搜索引擎里去寻找答案,一般情况下都能解决问题。 但是最近一段时间,我发现,搜索引擎搜出来的结果还没有利用短视频搜出来的答案更全面,短视频软件不仅可以显示AI整理出来的答案…

“先锋”西凤

执笔 | 文 清 编辑 | 古利特 制曲是酿酒的第一道工序,也是中国酿酒史上的一大创新,对白酒风味的影响至关重要。西凤酿酒人坚信“曲是酒之骨”,“曲”的品质决定酒的“骨气”,“酒曲”是酒体形成主题风味的基本定型元素和催化剂…

5分钟学会掼蛋

一、基本规则 1、用牌:总计108张,每人发牌27张。 2、牌型:八炸弹、七炸弹、六炸弹、五炸弹、四炸弹、天王炸、同花顺、顺子、钢板、三同张、三带二、单牌、对牌、三连对,注意掼蛋没有三带一的牌型。3、牌型大小:天王炸…

跟TED演讲学英文:What moral decisions should driverless cars make by Iyad Rahwan

What moral decisions should driverless cars make? Link: https://www.ted.com/talks/iyad_rahwan_what_moral_decisions_should_driverless_cars_make Speaker: Iyad Rahwan Date: September 2016 文章目录 What moral decisions should driverless cars make?Introduct…

electron 通信总结

默认开启上下文隔离的情况下 渲染进程调用主进程方法: 主进程 在 main.js 中, 使用 ipcMain.handle,添加要处理的主进程方法 const { ipcMain } require("electron"); 在 electron 中创建 preload.ts 文件,从 ele…

LangChain框架学习总结

目录 一、简介 二、概念 三、组件具体介绍 3.1 Models 3.1.1 LLMs 3.1.2 Chat Models 3.1.3 Text Embedding Modesl 3.1.4 总结 3.2 Prompts 3.2.1 LLM Prompt Template 3.2.1.1 自定义PromptTemplate 3.2.1.2 partial PromptTemplate 3.2.1.3 序列化PromptTemplat…

Python使用割圆法求π值

三国时期刘徽提出的割圆法有多牛掰,看这个:刘徽割圆术到底做了什么? - 知乎 用Python实现的该算法代码如下: #!/usr/bin/env python """使用割圆法计算π值Usage::$ python calc_circle_pi.py 20 # 参数20是迭代…

【Kubernetes系统与CKA(D)考试经验】

文章目录 应用程序部署模式的发展历程传统部署模式虚拟化部署模式容器部署模式容器部署模式的好处容器部署的弊端 容器编排Kubernetes网址Kubernetes架构互联网架构体系云原生(微服务容器化Devops不可变基础设施)Kubernetes架构Kubernetes组件 Kubernete…

【Docker】★★★

docker 的网络模式 ●host模式:使用 --nethost 指定 容器与宿主机共享网络命名空间、ip和端口 ●container模式:使用 --netcontainer:NAME_or_ID 指定 新建的容器共享已有容器的网络命名空间、ip和端口 ●none模式:使用 --netnone 指定 不进行…

数据结构——链表专题2

文章目录 一、返回倒数第k 个节点二、链表的回文结构三、相交链表 一、返回倒数第k 个节点 原题链接:返回倒数第k 个节点 利用快慢指针的方法:先让fast走k步,然后fast和slow一起走,直到fast为空,最后slow指向的结点就…

如何利用现货黄金避险功能来交易?

黄金自古以来就是投资者青睐的具有规避风险功能的投资品种,现货黄金作为以黄金为投资标的物的品种,自然也具备了规避风险的功能。如果出现了风险事件,避险情绪会为现货黄金带来颇为可观的行情,所以我们需要懂得如何利用现货黄金的…

智能AI摄像头项目

项目概要 硬件说明:易百纳rv1126 38板,易百纳GC2053摄像头,拓展版(自绘),屏幕驱动板(自绘),3.1inch mipi屏,FT5316触摸屏 开发环境 硬件分析 开发环境及sd…

代码随想录训练营31day-动态规划4

一、完全背包(参考博客) 和01背包区别在于物品可以无限次放入背包。完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。 因此在需要在遍历顺序上进行区别,参考代码随想录: 二、518.零钱兑换II 题目求的是组…

光端机(2)——光纤通信学习笔记九

学习笔记里面只关注基本原理和概念,复杂的公式和推导都没有涉及 光端机 光发射机 作用:实现电光转换。将来自电端机的电信号对光源发出的光波进行调制,然后将调制好的光信号耦合到光线中传输。 基本性能要求 1.合适的发光波长(光…

FreeBSD RISCV 在QEME中实践-网络配置

在前一篇文章中,我们一起进行了FreeBSD RISCV 在QEME中实践 现在,让我们配置好网络吧! 先上结论:用默认配置启动即可,网络就加载好了,只是不能ping罢了。因为不能ping,以为网络没通&#xff0…

如何使用ArcGIS Pro进行选房分析

无论是研究城市规划布局还是寻找理想的住房,都需要综合考虑购物、医疗、教育和休闲等多方面因素,此时我们的GIS软件就可以派上用场了,这里为大家介绍一下如何使用 ArcGIS Pro 进行选房分析,希望能对你有所帮助。 数据来源 教程所…

【iOS】NSOperation、NSOperationQueue

文章目录 前言一、NSOperation、NSOperationQueue 简介二、NSOperation、NSOperationQueue 操作和操作队列三、NSOperation四、NSOperationQueue五、NSOperationQueue 控制串行执行、并发执行六、 NSOperation 操作依赖七、NSOperation 优先级八、NSOperation、NSOperationQueu…

概率论 科普

符号优先级 概率公式中一共有三种符号:分号 ; 、逗号 , 、竖线 | 。 ; 分号代表前后是两类东西,以概率P(x;θ)为例,分号前面是x样本,分号后边是模型参数。分号前的 表示的是这个式子用来预测分布的随机变量x,分号后的…

土壤重金属含量分布、Cd镉含量、Cr、Pb、Cu、Zn、As和Hg、土壤采样点、土壤类型分布

土壤是人类赖以生存和发展的重要资源之一,也是陆地生态系统重要的组成部分。近年来, 随着我国城市化进程加快,矿产资源开发、金属加工冶炼、化工生产、污水灌溉以及不合理的化肥农药施用等因素导致重金属在农田土壤中不断富集。重金属作为土壤环境中一种具有潜在危害…