目录

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. Docker namespace,cgroup,镜像构建,数据持久化及Harbor安装、高可用配置

    1.Docker namespace 1.1 namespace介绍 namespace是Linux提供的用于分离进程树.网络接口.挂载点以及进程间通信等资源的方法.可以使运行在同一台机器上的不同服务 ...

  2. 面试官:Java 线程如何启动的?

    摘要:Java 的线程创建和启动非常简单,但如果问一个线程是怎么启动起来的往往并不清楚,甚至不知道为什么启动时是调用start(),而不是调用run()方法呢? 本文分享自华为云社区<Threa ...

  3. 【Pathview web】通路映射可视化

    前言 pathview是一个通路可视化友好的R包,最主要的是它支持多组学数据映射(基因/蛋白-代谢).自己用过它的R包,后来发现有网页版的,果断介绍给学员.因为不常用,记录要点,以后温习备用. 目前w ...

  4. Excel-转换单元格格式的函数或“方法”汇总

    14.转换单元格格式的函数或"方法"汇总 =value(单元格)  #转换为数值 =A1&""                   #转换A1为文本 = ...

  5. Oracle-where exists()、not exists() 、in()、not in()用法以及效率差异

    0.exists() 用法: select * from T1 where exists(select 1 from T2 where T1.a=T2.a) 其中 "select 1 fro ...

  6. c#跳转

    Response.Redirect(EditUrl("MEUID", lblMEUID.Text, "Page2", "PageOneMK" ...

  7. SQLITE_BTREE_H

    sqlite3.c, 237436行 = 全部源文件,找东西比多文件查找方便多了:-),字符串查找一点都不慢. 不要太害怕,SQLite说它的代码里有非常多是用来做数据完整性检查和测试的.但愿B树,虚 ...

  8. acute

    In Euclidean geometry, an angle is the figure formed by two rays, called the sides of the angle, sha ...

  9. Bootstrap-table动态表格

    在开发中遇到一个需要动态生成table的需求,包括表头和数据.在调试的过程中遇到很多问题,包括数据分页,解决之后记录一下. 如下代码的数据加载流程: ①表头是动态的,在初始化table之前需要调一次后 ...

  10. Can we call an undeclared function in C++?

    Calling an undeclared function is poor style in C (See this) and illegal in C++. So is passing argum ...