Linux高性能I/O框架库Libevent介绍

2023-11-12

C/C++Linux服务器开发/后台架构师知识体系

这篇文章主要讲一下Libevent库的内容,顺便对I/O库整体做个介绍。

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

  • I/O事件
  • 信号
  • 定时事件

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

  • 统一事件源。很明显,统一处理这三类事件既能使代码简单易懂,又能避免一些潜在的逻辑错误。
  • 可移植性。不同的操作系统具有不同的I/O复用方式,比如Solarisdev/poll文件,FressBSDkqueue机制,Linuxepoll系统调用
  • 对并发编程的支持,在多进程和多线程环境下,我们需要考虑各执行实体如何协同处理客户连接、信号和定时器,以避免竞态条件。

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

I/O框架库概述

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

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

  • 句柄Handle
  • 事件多路分发器EventDemultiplexer
  • 事件处理器Eventhandler
  • 具体的事件处理器ConcreteEventHandler
  • Reactor

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

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

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

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

  • handle_events:该方法执行事件循环。它重复如下过程:等待事件,然后依次处理所有就绪事件对应的事件处理器。
  • register_handler: 该方法调用事件多路分发器的register_event方法来往事件多路分发器中注册一个事件。
  • remove_handler:该方法调用事件多路分发器的remove_event方法来往删除事件多路分发器中注册一个事件。

I/O框架库的工作时序如下:
在这里插入图片描述

Libevent源码分析

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

  • 跨平台支持
  • 统一事件源
  • 线程安全
  • 基于Reactor模式的实现

一个实例

下面是用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库的主要逻辑:

  • 调用event_base_new函数创建event_base对象。一个event_base相当于一个Reactor实例。

  • 创建具体的事件处理器,并设置它们所从属的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参数指定行

其中:

  • 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  0x10     /*永久事件*/
    /*边缘触发事件,需要I/O复用系统调用支持,比如epoll */
    #define EV_ET       0x20

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

  • cb参数指定目标事件对应的回调函数,相当于事件处理器handle_event方法.
  • arg则是Reactor传递给回调函数的参数。

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

  • 事件指的是一个句柄上绑定的事件,比如文件描述符 0 上的可读事件
  • 事件处理器,也就是event结构提类型的对象,除了包含事件必须具备的两个要素(句柄和事件类型)外,还有很多其他成员,比如回调函数
  • 事件由事件多路分发器管理,事件处理器则由事件队列管理,事件队列包括多种,比如event_base中的注册事件队列。
  • 事件循环对一个被激活事件(就绪事件)的处理,指的是执行该事件对应的事件处理器中的回调函数。

调用event_add函数,将事件处理器添加到注册事件队列中,并将该事件处理器对应的事件添加到事件多路分发器中。even_add函数相当于Reactor中的register_handler方法。
调用event_base_dispatch函数来执行事件循环
事件循环结束后,使用*_free系列释放系统资源

源代码组织结构

  • github地址:https://github.com/libevent/libevent
  • 头文件目录include/event2。该目录是自Libevent主板本升级到2.0之后引入的,是提供给应用程序使用的,比如event.h头文件是核心函数,http.h头文件提供HTTP协议相关服务,rpc.h头文件提供远程过程调用支持。
  • 源码根目录下的头文件。这些头文件分为两类:
  • 一类是对include/event2目录下的部分头文件的包装
  • 另外一类是供Libevent内部使用的辅助性头文件,它们的文件名都具有*-internal.h的形式。
  • 通用数据目录compat/sys。该目录下仅有一个文件----queue.h。它封装了跨平台的基础数据结构,包括单向链表、双向链表、队列、尾队列和循环队列。
  • sample目录。提供一些示例代码
  • test目录。提供一次额测试代码
  • WIN32-Code。提供Windows平台上的一些专用代码。
  • event.c文件。该文件时间Libevent的整体框架,主要是eventevent_base两个结构体的相关操作。
  • debpoll.ckqueue.cevport.cselect.cwin32select.cpoll.cepoll.c文件。它们分别封装了如下I/O复用机制:/dev/pollkqueueevent portsPOSIX selectWindows selectpollepoll。这些文件的主要内容相似,都是针对结构体eventop所定义的接口函数的具体实现。
  • minheap-internal.h:该文件实现了一个事件堆,以提供对定时事件的支持。
  • signal.c:提供对信号的支持。其内容也是针对结构体eventop所定义的接口函数的具体实现
  • evmap.c文件:它维护句柄(文件描述符或信号)与时间处理器的映射关系
  • event_tagging.c:提供往缓冲区中添加标记数据,比如一个正数,以及从缓冲区中读取标记数据的函数
  • event_iocp文件:提供对Windows IOCP(Input/Output Completion Port,输入输出完成端口)的支持
  • buffer*.c文件:提供对网络I/O缓冲的控制,包括:输入输出数据过滤,传输速率限制,使用SSL(Secure Sockets Layer)协议对应用数据进行保护,以及零拷贝文件传输等。
  • evthread*.c文件:提供对多线程的支持
  • listener.c:封装了对监听socket的操作,包括监听连接和接受连接
  • logs.c文件。它是Libevent的日志文件系统
  • evutil.cevutil_rand.cstrlcpy.carc4random.c文件:提供了一些基本操作,比如生成随机数、获取socket地址信息、读取文件、设置socket属性等
  • evdns.c、http.cevrpc.c地址信息:分别提供了对DNS协议、HTTP协议和RPC(Remote Procddure Call,远程过程调用)协议的支持
  • epoll_sub.c文件,该文件未见使用

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

以上就是关于Linux中高性能I/O框架库Libevent的相关介绍了,希望对大家有所帮助。

推荐:

视频:高性能网络库 libevent 讲解

Linux服务器开发/高级架构师 系统学习公开课地址:https://ke.qq.com/course/417774?flowToken=1031343

欢迎朋友们加入C/C++Linux服务器开发/高级架构师群:960994558
群内提供免费的C/C++Linux服务器开发/高级架构师学习资料资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux高性能I/O框架库Libevent介绍 的相关文章

  • 强制卸载 NFS 安装目录 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案
  • 使用 grep 查找包含所有搜索字符串的行

    我有一个文件 其中包含很多与此类似的行 id 2796 some model Profile message type MODEL SAVE fields account 14 address null modification times
  • 如何使用 xterm.js 创建基于 Web 的终端以 ssh 进入本地网络上的系统

    我偶然发现了这个很棒的图书馆xterm js https xtermjs org 这也是 Visual Studio Code 终端的基础 我有一个非常普遍的问题 我想通过基于网络的终端 不在网络中 可能位于 aws 服务器上 访问本地网络
  • 应用程序无缘无故地被杀死。怀疑 BSS 高。如何调试呢?

    我已经在CentOs6 6中成功运行我的应用程序 最近 硬件 主板和内存 更新了 我的应用程序现在毫无理由地被杀死 root localhost PktBlaster PktBlaster Killed 文件和 ldd 输出 root lo
  • 如何禁用 GNOME 桌面屏幕锁定? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何阻止 GNOME 桌面在几分钟空闲时间后锁定屏幕 我已经尝试过官方手册了在红帽 https access redhat com doc
  • Jenkins中找不到环境变量

    我想在詹金斯中设置很多变量 我试过把它们放进去 bashrc bash profile and profile of the jenkins用户 但 Jenkins 在构建发生时找不到它们 唯一有效的方法是将所有环境变量放入Jenkinsf
  • chown:不允许操作

    我有问题 我需要通过 php 脚本为系统中的不同用户设置文件所有者权限 所以我通过以下命令执行此操作 其中 1002 是系统的用户 ID file put contents filename content system chown 100
  • 在哪里可以找到并安装 pygame 的依赖项?

    我对 Linux 比较陌生 正在尝试安装 python 的 pygame 开发环境 当我运行 setup py 时 它说我需要安装以下依赖项 我找到并安装了其中之一 SDL 然而 其他人则更加难以捉摸 Hunting dependencie
  • 使用 sed 更新 xml 属性(Windows + cygwin 和 Linux)?

    我需要使用 sed 命令对 xml 文件进行更新 但我在这方面遇到了麻烦 它需要在 Windows 使用 cygwin 和 Linux 上运行 XML 具有以下元素
  • 域套接字“sendto”遇到“errno 111,连接被拒绝”

    我正在使用域套接字从另一个进程获取值 就像 A 从 B 获取值一样 它可以运行几个月 但最近 A 向 B 发送消息时偶尔会失败 出现 errno 111 连接被拒绝 我检查了B域套接字绑定文件 它是存在的 我也在另一台机器上做了一些测试 效
  • 加载数据infile,Windows和Linux的区别

    我有一个需要导入到 MySQL 表的文件 这是我的命令 LOAD DATA LOCAL INFILE C test csv INTO TABLE logs fields terminated by LINES terminated BY n
  • 添加要在给定命令中运行的 .env 变量

    我有一个 env 文件 其中包含如下变量 HELLO world SOMETHING nothing 前几天我发现了这个很棒的脚本 它将这些变量放入当前会话中 所以当我运行这样的东西时 cat env grep v xargs node t
  • 无法使用 wget 在 CentOS 机器上安装 oracle jdk

    我想在CentOS上安装oracle java jdk 8 我无法安装 java jdk 因为当我尝试使用命令安装 java jdk 时 root ADARSH PROD1 wget no cookies no check certific
  • 如何在apache 2.4.6上安装apxs模块

    我刚刚用过apt get update我的 apache 已更新为2 4 6 我想安装 apxs 来编译模块 但收到此错误 The following packages have unmet dependencies apache2 pre
  • 有谁知道在哪里定义硬件、版本和序列号。 /proc/cpuinfo 的字段?

    我想确保我的 proc cpuinfo 是准确的 目前它输出 Hardware am335xevm Revision 0000 Serial 0000000000000000 我可以在代码中的哪里更改它以给出实际值 这取决于 Linux 的
  • docker容器大小远大于实际大小

    我正在尝试从中构建图像debian latest 构建后 报告的图像虚拟大小来自docker images命令为 1 917 GB 我登录查看尺寸 du sh 大小为 573 MB 我很确定这么大的尺寸通常是不可能的 这里发生了什么 如何获
  • 多处理:仅使用物理核心?

    我有一个函数foo它消耗大量内存 我想并行运行多个实例 假设我有一个有 4 个物理核心的 CPU 每个核心有两个逻辑核心 我的系统有足够的内存来容纳 4 个实例foo并行但不是 8 个 此外 由于这 8 个核心中的 4 个是逻辑核心 我也不
  • ftrace:仅打印trace_printk()的输出

    是否可以只转储trace printk 输出于trace文件 我的意思是过滤掉函数跟踪器 或任何其他跟踪器 中的所有函数 一般来说 您可以在选项目录中关闭选项 sys kernel debug tracing options Use ls显
  • 查找哪个程序运行另一个程序

    我有一个 NAS 运行在 Redhat Linux 的有限版本上 我按照指示破解了它 这样我就可以访问 shell 这很有帮助 我还做了一些修改 其他人也做过修改 除了一个问题之外 它们似乎都工作得很好 不知何故 每隔 22 天 系统就会关
  • 如何确保应用程序在 Linux 上持续运行

    我试图确保脚本在开发服务器上保持运行 它会整理统计数据并提供网络服务 因此它应该会持续存在 但一天中有几次 它会因未知原因而消失 当我们注意到时 我们只需再次启动它 但这很麻烦 并且某些用户没有权限 或专有技术 来启动它 作为一名程序员 我

随机推荐

  • 韩式多用动态图(甜蜜女孩)

  • 离线在Jenkins安装CoBOT安装插件

    最近在某金融客户做POC 把CoBOT安装在Jenkins上面 当前Jenkins版本没有任何插件 安装后由于是云桌面没有连接互联网或已经设置访问策略 无法进行在线安装插件 所以只能下载插件后再安装 在网络上搜索Jenkins插件 下载到两
  • 【SpringCloud】application.yml和 bootstrap.yml 区别

    1 首先yml和properties文件都是属于配置文件 功能一样 主要是区别于application和bootstrap的加载顺序 Bootstrap yml bootstrap properties 在application yml a
  • caffe问题Check failed: registry.count(type) == 1 (0 vs. 1) Unknown layer type: Python

    caffe中输入层使用python时 出现问题 Check failed registry count type 1 0 vs 1 Unknown layer type Python 解决方法 在caffe目录下Makefile confi
  • va_start和va_end使用详解

    本文主要介绍va start和va end的使用及原理 介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理 1 在C中 当我们无法列出传递函数的所有实参的类型和数目时 可以用省略号指定参数表 void foo void foo par
  • 手机抓包fiddler配置及使用教程

    本文基于Fiddler4讲解基本使用 fiddler抓包原理 注意 Fiddler 是以代理web服务器的形式工作的 它使用代理地址 127 0 0 1 端口 8888 当Fiddler退出的时候它会自动注销 这样就不会影响别的 程序 不过
  • libsvm库简介及使用

    libsvm是基于支持向量机 support vector machine SVM 实现的开源库 由台湾大学林智仁 Chih Jen Lin 教授等开发 它主要用于分类 支持二分类和多分类 和回归 它的License是BSD 3 Claus
  • Terdata 基础 第三课(参数宏)

    1 宏不是ANSI标准支持的 但大部分RDBMS都支持宏 在Teradata中 在ANSI和BTET缺省模式下都可以创建和执行宏 只不过在ANSI模式下会给出警告信息 1 1 参数宏 宏中可以包含可替代值的变量 CREATE MACRO d
  • linux shell 按行循环读入文件方法

    linux shell 按行循环读入文件常用代码如下 bin bash printf n echo cat file whiel read line cat test txt while read line do echo line don
  • wofstream,wcout无法输出unicode的真相

    之前我转载过一篇ofstream和wofstream与中文输出问题 让我初步知道如何解决这类问题 第一次我没有在意 按照文章中做的方法去做 然后程序就运行正常了 我试图去记住这些规则 但是我后来发现 太难了 以至于我在最近一次使用到 std
  • 程序编程代码大全_CNC加工中心程序代码大全,数控加工必备!

    数控机床的可编程功能分为两类 一类用来实现刀具轨迹控制即各进给轴的运动 如直线 圆弧插补 进给控制 坐标系原点偏置及变换 尺寸单位设定 刀具偏置及补偿等 这一类功能被称为准备功能 以字母G以及两位数字组成 也被称为G代码 另一类功能被称为辅
  • 免费赠票

    Cloud Ace 受邀参加 GTC2022 全球流量大会 助力中国企业扬帆出海 大会将在 2023 年 2 月 28 日 3 月 1 日举行 地点就在福田会展中心 6 号展馆 大会门票实行收费制 您可以扫码填写 Cloud Ace 的报名
  • GLSL语言基础

    定义 GLSL释义叫做OpenGL着色器编程语言 是为图形计算量身定制的 它包含一些针对向量和矩阵操作的有用特性 变量名字 变量名称的命名规范与C语言相同 可以使用字母 数字 以及下划线来组成变量的名字 但数字不能作为变量名称的第一个字符
  • OBS直播软件-简介

    转自 https jingyan baidu com article e2284b2b90c4dee2e6118dd3 html OBS直播软件是一款国外开发的用于网络直播的软件 本篇OBS教程主要介绍OBS下载和初级应用 工具 原料 一台
  • Open3D 最小二乘拟合空间直线(方法一)

    目录 一 算法原理 1 空间直线 2 最小二乘法拟合 二 代码实现 三 结果展示 本文由CSDN点云侠原创 原文链接 如果你不是在点云侠的博客中看到该文章 那么此处便是不要脸的爬虫 一 算法原理 1 空间直线 x
  • MySQL环境搭建利器---Sandbox

    MySQL环境搭建利器 Sandbox https metacpan org pod MySQL Sandbox http mysqlsandbox net https www cnblogs com gomysql p 3767445 h
  • Java中Arrays类的常用方法

    Java中Arrays类的常用方法 Arrays类位于 java util 包中 主要包含了操作数组的各种方法 import java util Arrays Arrays fill 填充数组 int arr new int 5 新建一个大
  • pycharm使用中的小tip

    1 双击shift会弹出全局搜索功能 关闭 双击shift 打开全局搜索 action gt registry 找到ide suppress dounle hangler勾上 重新打开 ctrl shift a 2 快速格式化代码 ctrl
  • pytorch 实现SSD详细理解 (一)vgg和特征图的提取

    摘要 本章就开始进入SSD的学习 通过学习这些基础的目标检测算法更好的对比理解其它算法 多看几种代码的写法更容易找到适合自己书写的套路 ssd网络的6个特征图 ssd采用的是vgg16的特征提取 在vgg16中提取二个特征图 之后又通过额外
  • Linux高性能I/O框架库Libevent介绍

    C C Linux服务器开发 后台架构师知识体系 这篇文章主要讲一下Libevent库的内容 顺便对I O库整体做个介绍 Linux服务器程序必须处理的三类事件 I O事件 信号 定时事件 在处理这三类事件时我们通常需要考虑如下三个问题 统