目录

Libevent缓冲区类型

Libevent缓冲区结构

缓冲区的读出与写入

缓冲区的读入与写出

缓冲区水位机制

缓冲区回调机制

延迟回调机制
Libevent缓冲区类型

       Libevent中提供了多种类型的缓冲区:基于套接字的缓冲区、针对Windows IOCP的bufferevent、在传输和接收数据之前进行数据处理(比如压缩)的过滤型缓冲区和成对传输的缓冲区。本文及后面的内容都仅针对基于套接字的缓冲区展开分析。
Libevent缓冲区结构

       Libevent实际上是由链表来实现一个缓冲区的,链表中的每一个结点都用来存放数据,整个缓冲区的数据也就存放在这一个个的链表结点之中,链表结点由evbuffer_chain数据类型定义。

       因此,整个链表就可以看做是一个缓冲区的数据集合,而管理整个链表则是由evbuffer数据类型实现,在该数据类型中可以指出缓冲区的头尾结点以及最后一个带数据的结点。

       通过上述两种数据结构,也就可以实现对缓冲区的增加删除数据等功能。

       对于每一个文件描述符,Libevent都为其设定了读缓冲区和写缓冲区,也就是说每一个fd都对应两个缓冲区。Libevent使用bufferevent数据类型来管理一个fd所对应的读写缓冲区及其相关信息。如下所示。

缓冲区的读出与写入

        缓冲区的读出与写入是指用户与读写缓冲区之间的交互:当用户需要从fd中读取数据时,实际上是从读缓冲区中读出数据;当用户需要向fd中写入数据时,实际上是向写缓冲区中写入数据。Libevent为用户的读和写都设置了接口(bufferevent_read和bufferevent_write),当用户调用这些接口时,并不是真的就把数据写到fd的内核缓冲区中或者从fd的内核缓冲区中读出。那么,知道了用户和缓冲区之间的交互,那缓冲区和真正的fd内核缓冲区之间又是如何交互的呢?
缓冲区的读入与写出

        缓冲区的读入与写出是指缓冲区与fd的内核缓冲区进行交互:读缓冲区从fd的内核缓冲区中读入数据,写缓冲区将数据写出到fd的内核缓冲区。读入和写出数据的时机,是靠libevent的基本事件处理框架来判断的。依然是监听fd的可读和可写事件,当fd可读时,就会触发相应读监听事件(bufferevent中的ev_read),回调函数就会把数据从fd的内核缓冲区中读到读缓冲区中;当fd可写时,就会触发相应的写监听事件(bufferevent中的ev_write),回调函数就会把数据从写缓冲区中写到fd的内核缓冲区中。
缓冲区水位机制

        对于每一个缓冲区,Libevent都设置了相应的高低水位。所谓“水位”,实际上就是对每个缓冲区设置的高低阈值,用来衡量缓冲区中的数据量。Libevent并未使用写缓冲区高水位,因此实际上有以下三种水位:

    读高水位:当读缓冲区中的数据量达到高水位,说明此时读缓冲区链表太长,此时就不应该再从fd读取数据了;

    读低水位:如果从fd中读取数据之后,读缓冲区的数据量低于低水位,相当于“几乎没读到什么数据”,那么bufferevent就不会去关注这次读取操作;

    写低水位:如果向fd中写出数据之后,写缓冲区的数据量高于低水位,相当于“几乎没写出什么数据”,那么bufferevent就不会去关注这次写出操作。

缓冲区回调机制

       Libevent总是在一些“应当进行一些处理”的时候,调用回调函数。

       前面设置了水位,那我们怎么知道缓冲区什么时候达到/超过/低于水位了呢?这个时候就通过回调函数来实现:当缓冲区的数据量达到了相应水位,那么就应该进行相应的回调来执行一些特殊的处理。举个例子,当读缓冲区数据量达到或超过读高水位,那么就应当停止从fd中读取数据,而这个“停止读取”的行为,就在回调函数中实现。

       除此之外,Libevent还为每个缓冲区维护了一个回调队列,提供了用户向回调队列中添加或删除回调函数的接口,这些回调函数会在每次缓冲区发生变化的时候,调用回调函数,并告诉回调函数“这次缓冲区变化增加/减少了多少数据”。
延迟回调机制

       由于用户可以向缓冲区中的回调队列任意添加回调函数,所以无法知道用户添加的回调函数到底要做什么,而用户也不知道Libevent中内置的回调函数何时调用,这样一来就可能存在用户回调和内置回调之间的递归调用,从而可能造成栈溢出。举个例子:如果用户添加了一个回调函数A,A会在缓冲区空的时候向缓冲区中写入数据,另一个回调函数B,会在缓冲区满的时候从缓冲区中抽取数据,由于缓冲区一旦改变就会立刻调用回调队列中的所有函数,因此就有可能A在返回之前,缓冲区处理回调队列时又调用了函数B,如果依赖关系足够复杂,B可能又调用回A,这样就会在前面的函数A还没返回,其他函数就开始调用,从而造成栈溢出。

       因此Libevent采用延迟回调机制,如果现在需要立刻调用回调函数,Libevent就会用一个“代表”来“代表”这些回调函数,把“代表”放到主循环的激活队列中。当主循环处理这个“代表”时,这个时候再回来调用所有回调函数。这样的好处在于,当缓冲区改变时并不会立刻再次调用回调队列中的函数,而是会进行“延时调用”,当再次处理回调队列的时候,函数A已经返回了,这样也就防止了多次递归的情况,也就避免了栈溢出。

 

 

       后面的文章,再来分析一下Libevent是如何去实现这些的。
————————————————
版权声明:本文为CSDN博主「HerofH_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_28114615/article/details/100037869

libevent源码学习(17):缓冲管理框架的更多相关文章

  1. libevent源码学习

    怎么快速学习开源库比如libevent? libevent分析 - sparkliang的专栏 - 博客频道 - CSDN.NET Libevent源码分析 - luotuo44的专栏 - 博客频道 ...

  2. libevent源码学习(11):超时管理之min_heap

    目录min_heap的定义向min_heap中添加eventmin_heap中event的激活以下源码均基于libevent-2.0.21-stable.       在前文中,分析了小顶堆min_h ...

  3. libevent源码学习(10):min_heap数据结构解析

    min_heap类型定义min_heap函数构造/析构函数及初始化判断event是否在堆顶判断两个event之间超时结构体的大小关系判断堆是否为空及堆大小返回堆顶event分配堆空间堆元素的上浮堆元素 ...

  4. libevent源码学习(8):event_signal_map解析

    目录event_signal_map结构体向event_signal_map中添加event激活event_signal_map中的event删除event_signal_map中的event以下源码 ...

  5. libevent源码学习(9):事件event

    目录在event之前需要知道的event_baseevent结构体创建/注册一个event向event_base中添加一个event设置event的优先级激活一个event删除一个event获取指定e ...

  6. libevent源码学习(6):事件处理基础——event_base的创建

    目录前言创建默认的event_baseevent_base的配置event_config结构体创建自定义event_base--event_base_new_with_config禁用(避免使用)某一 ...

  7. libevent源码学习(2):内存管理

    目录 内存管理函数 函数声明 event-config.h 函数定义 event_mm_malloc_ event_mm_calloc_ event_mm_strdup_ event_mm_reall ...

  8. libevent源码学习(1):日志及错误处理

    目录 错误处理函数 函数声明 __attribute__指令 函数定义 可变参数宏 _warn_helper函数 日志处理 event_log日志处理入口 日志处理回调函数指针log_fn 设置日志处 ...

  9. libevent源码学习(7):event_io_map

    event_io_map 哈希表操作函数 hashcode与equals函数 哈希表初始化 哈希表元素查找 哈希表扩容 哈希表元素插入 哈希表元素替换 哈希表元素删除 自定义条件删除元素 哈希表第一个 ...

随机推荐

  1. Golang进阶,揉碎数据库中间件,干货满满!

    目录 必读 一.Centos7.Mac安装MySQL 二.主从复制原理 2.1.基于binlog_filename + position 2.2.基于GTID 三.my.cnf 四.测试SQL 五.中 ...

  2. vue-router的安装和使用

    第一种方式(简单):新建项目时用 vue-cli 手动选择 router 安装. 1.用键盘 ↓ 移动选择第三项" Manually select features"(手动配置): ...

  3. AT4168 [ARC100C] Or Plus Max

    从\(whk\)回来了. 考虑我们需要维护一个子集的信息. 对于二进制的子集信息维护有一个很经典的操作: 高维前缀和. AT4168 [ARC100C] Or Plus Max // Problem: ...

  4. LOJ #6207 - 米缇(杜教筛+拉格朗日插值)

    LOJ 题面传送门 首先将 \(\sigma_k(ij)\) 展开: \[\sigma_k(ij)=\sum\limits_{x\mid i}\sum\limits_{y\mid j}[x\perp ...

  5. CF 585 E Present for Vitalik the Philatelist

    CF 585 E Present for Vitalik the Philatelist 我们假设 $ f(x) $ 表示与 $ x $ 互质的数的个数,$ s(x) $ 为 gcd 为 $ x $ ...

  6. 莫比乌斯反演&各种筛法

    不学莫反,不学狄卷,就不能叫学过数论 事实上大概也不是没学过吧,其实上赛季头一个月我就在学这东西,然鹅当时感觉没学透,连杜教筛复杂度都不会证明,所以现在只好重新来学一遍了(/wq 真·实现了水平的负增 ...

  7. 【R】write.table输出数据带有行名?

    目录 问题 解决一 解决二 问题 这个问题应该很常见吧.R中输出数据框时,想要把行名和列名都输出.如果直接输出的话,输出的结果列名会往前移动一位,这显然不是我们想要的. 直接上例子: > a = ...

  8. 【机器学习与R语言】11- Kmeans聚类

    目录 1.理解Kmeans聚类 1)基本概念 2)kmeans运作的基本原理 2.Kmeans聚类应用示例 1)收集数据 2)探索和准备数据 3)训练模型 4)评估性能 5)提高模型性能 1.理解Km ...

  9. 【3】蛋白鉴定软件之Mascot

    目录 1.简介 2.配置 2.1在线版本 2.2 服务器版本 3.运行 3.1 在线版本 3.2 服务器版本 4.结果 1.简介 Mascot是非常经典的蛋白鉴定软件,被Frost & Sul ...

  10. nginx_location

    Nginx location 配置语法 1. location [ = | ~ | ~* | ^~ ] uri { ... } 2. location @name { ... } location 配 ...