目录

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. Codeforces 1404D - Game of Pairs(构造)

    Codeforces 题面传送门 & 洛谷题面传送门 首先注意到 \(\sum\limits_{i=1}^{2n}i=\dfrac{2n(2n+1)}{2}=n(2n+1)\equiv n\p ...

  2. GWAS分析结果中pvalue/p.ajust为0时如何处理?

    在GWAS分析的结果中,偶尔会遇到到pvalue为0的SNP位点,这时如果直接做曼哈顿或QQ图,会出错,因为log0无意义. 此时,该如何处理? 如果你用的是Plink1.9来做的GWAS,可加一个参 ...

  3. R 语言实战-Part 5-1笔记

    R 语言实战(第二版) part 5-1 技能拓展 ----------第19章 使用ggplot2进行高级绘图------------------------- #R的四种图形系统: #①base: ...

  4. PHP生成EXCEL,支持多个SHEET

    PHP生成EXCEL,支持多个SHEET 此版本为本人演绎版本,原版本地址http://code.google.com/p/php-excel/ php-excel.class.php: <?p ...

  5. A Child's History of England.52

    'Arthur,' said the King, with his wicked eyes more on the stone floor than on his nephew, 'will you ...

  6. Learning Spark中文版--第四章--使用键值对(2)

    Actions Available on Pair RDDs (键值对RDD可用的action)   和transformation(转换)一样,键值对RDD也可以使用基础RDD上的action(开工 ...

  7. android studio 使用 aidl(二)异步回调

    基础使用请移步 android studio 使用 aidl (一) 首先建立在server端建立两个aidl文件 ITaskCallback.aidl 用于存放要回调client端的方法 // IT ...

  8. PhoneGap本地将html打包成安卓App

    PhoneGap的在线打包有大小限制,超过30M的包无法在线打包.当然,可以把包里面的图片.声音文件去掉,然后打包.下载以后,解包,重新打包并签名.蛮麻烦的. 本地打包的简单方法如下: 下载安装Jav ...

  9. spring注解-bean生命周期

    https://www.jianshu.com/p/70b935f2b3fe bean的生命周期 bean创建---初始化----销毁的过程 容器管理bean的生命周期 对象创建:容器启动后调用bea ...

  10. 【编程思想】【设计模式】【行为模式Behavioral】迭代器模式iterator

    Python版 https://github.com/faif/python-patterns/blob/master/behavioral/iterator.py #!/usr/bin/env py ...