DPDK ring库:环形缓冲区的解剖
目录
ring库
FreeBSD *中的Ring实施参考
Linux *中的无锁环形缓冲区
附加功能
名称
用例
环形缓冲区的解剖
单一生产者入队
单一消费者出队
多个生产者入队
模数32位索引
生产者/消费者同步模式
MP / MC(默认一项)
SP / SC
MP_RTS / MC_RTS
MP_HTS / MC_HTS
环窥API
参考资料
ring库
环允许管理队列。除了具有无限大小的链表之外,rte_ring具有以下属性:
- 先进先出
- 最大大小是固定的,对象存储在表中
- 对象可以是指针或4字节大小倍数的元素
- 无锁实现
- 多消费者或单消费者出队
- 多生产者或单生产者入队
- 批量出队-如果成功,则使指定数量的对象出队;否则失败
- 批量入队-如果成功,则使指定数量的对象入队;否则失败
- 突发出队-如果无法满足指定的计数,则出队最大可用对象
- 突发入队-如果无法满足指定的数量,则排入最大可用对象
与链接列表队列相比,此数据结构的优点如下:
- 快点; 只需要一个32位的Compare-And-Swap指令,而不是几个指针大小的Compare-And-Swap指令。
- 比完整的无锁队列简单。
- 适用于批量入队/出队操作。当对象存储在表中时,多个对象的出队将不会产生与链接队列中一样多的高速缓存未命中。同样,许多对象的批量出队不比简单对象的出队花费更多。
缺点:
- 大小是固定的
- 就内存而言,拥有多个环比链接列表队列要花费更多。一个空环至少包含N个对象。
图中显示了Ring的简化表示,其中消费者和生产者的头和尾指针指向存储在数据结构中的对象。
rte_ring结构体
/*** An RTE ring structure.** The producer and the consumer have a head and a tail index. The particularity* of these index is that they are not between 0 and size(ring). These indexes* are between 0 and 2^32, and we mask their value when we access the ring[]* field. Thanks to this assumption, we can do subtractions between 2 index* values in a modulo-32bit base: that's why the overflow of the indexes is not* a problem.*/
struct rte_ring {/** Note: this field kept the RTE_MEMZONE_NAMESIZE size due to ABI* compatibility requirements, it could be changed to RTE_RING_NAMESIZE* next time the ABI changes*/char name[RTE_MEMZONE_NAMESIZE] __rte_cache_aligned; /**< Name of the ring. */int flags; /**< Flags supplied at creation. */const struct rte_memzone *memzone;/**< Memzone, if any, containing the rte_ring */uint32_t size; /**< Size of ring. */uint32_t mask; /**< Mask (size-1) of ring. */uint32_t capacity; /**< Usable size of ring */char pad0 __rte_cache_aligned; /**< empty cache line *//** Ring producer status. */struct rte_ring_headtail prod __rte_cache_aligned;char pad1 __rte_cache_aligned; /**< empty cache line *//** Ring consumer status. */struct rte_ring_headtail cons __rte_cache_aligned;char pad2 __rte_cache_aligned; /**< empty cache line */
};
FreeBSD *中的Ring实施参考
以下代码已添加到FreeBSD 8.0中,并在某些网络设备驱动程序中使用(至少在Intel驱动程序中使用):
- FreeBSD中的bufring.h
- FreeBSD中的bufring.c
Linux *中的无锁环形缓冲区
以下是描述Linux无锁环形缓冲区设计的链接。
附加功能
名称
环由唯一名称标识。无法创建两个具有相同名称的环(如果尝试这样做,rte_ring_create()将返回NULL)。
用例
Ring库的用例包括:
- DPDK中的应用程序之间的通信
- 由内存池分配器使用
环形缓冲区的解剖
本节说明环形缓冲区的工作方式。环形结构由两个头对和尾对组成。一种由生产者使用,另一种由消费者使用。以下各节的图将它们称为prod_head,prod_tail,cons_head和cons_tail。
每个图代表环的简化状态,它是一个圆形缓冲区。函数局部变量的内容显示在图的顶部,而环结构的内容显示在图的底部。
单一生产者入队
本节说明生产者将对象添加到环时会发生什么。在此示例中,仅修改了生产者的头和尾(prod_head和prod_tail),并且只有一个生产者。
初始状态是使prod_head和prod_tail指向同一位置。
入队第一步
首先,将ring-> prod_head和ring-> cons_tail复制到局部变量中。prod_next局部变量指向表的下一个元素,或者在批量入队的情况下指向多个元素。
如果环中没有足够的空间(通过检查cons_tail可以检测到),它将返回错误。
入队第二步
第二步是修改环结构中的ring-> prod_head以指向与prod_next相同的位置。
添加的对象被复制到环(obj4)中。
入队最后一步
将对象添加到环中后,将修改环结构中的ring-> prod_tail使其指向与ring-> prod_head相同的位置。入队操作完成。
单一消费者出队
本节说明了当使用者将对象从环中出队时会发生什么。在此示例中,仅修改了消费者的头和尾(cons_head和cons_tail),并且只有一个消费者。
初始状态是使cons_head和cons_tail指向同一位置。
出队第一步
首先,将ring-> cons_head和ring-> prod_tail复制到局部变量中。cons_next局部变量指向表的下一个元素,或者在批量出队的情况下指向多个元素。
如果环中没有足够的对象(通过检查prod_tail可以检测到),它将返回错误。
出队第二步
第二步是修改ring结构中的ring-> cons_head以指向与cons_next相同的位置。
出队对象(obj1)复制到用户给定的指针中。
出队最后一步
最后,将ring结构中的ring-> cons_tail修改为指向与ring-> cons_head相同的位置。出队操作完成。
多个生产者入队
本节说明当两个生产者同时将对象添加到环时会发生什么。在此示例中,仅修改了生产者的头和尾(prod_head和prod_tail)。
初始状态是使prod_head和prod_tail指向同一位置。
多个生产者进入第一步
在两个内核上,ring-> prod_head和ring-> cons_tail都复制到局部变量中。prod_next局部变量指向表的下一个元素,或者在批量入队的情况下指向多个元素。
如果环中没有足够的空间(通过检查cons_tail可以检测到),它将返回错误。
多个生产者加入第二步
第二步是修改ring结构中的ring-> prod_head以指向与prod_next相同的位置。此操作使用“比较和交换”(CAS)指令完成,该指令自动执行以下操作:
- 如果ring-> prod_head与局部变量prod_head不同,则CAS操作失败,并且代码在第一步重新启动。
- 否则,将ring-> prod_head设置为本地prod_next,CAS操作成功,然后继续处理。
在该图中,操作在内核1上成功完成,而第一步在内核2上重新启动。
多个生产商加入第三步
成功在核心2上重试CAS操作。
核心1更新了ring(obj4)的一个元素,核心2更新了另一个(obj5)的元素。
多个生产者进入第四步
每个内核现在都想更新ring-> prod_tail。仅当ring-> prod_tail等于prod_head局部变量时,内核才能更新它。这仅在内核1上成立。操作已在内核1上完成。
多个生产者进入最后一步
内核1更新了ring-> prod_tail之后,内核2也可以对其进行更新。该操作也已在核心2上完成。
模数32位索引
在前面的图中,prod_head,prod_tail,cons_head和cons_tail索引由箭头表示。在实际的实现中,这些值不介于0和size(ring)-1之间,如所假设的那样。索引在0到2 ^ 32 -1之间,访问对象表(环本身)时,我们会屏蔽它们的值。32位模还意味着如果结果超出32位数字范围,对索引的操作(例如加/减)将自动执行2 ^ 32模。
以下是两个示例,有助于说明如何在环中使用索引。
注意:为了简化说明,使用16位模运算而不是32位模运算。另外,这四个索引被定义为无符号的16位整数,而在更实际的情况下,这是无符号的32位整数。
Modulo 32位索引-示例1
该环包含11000个条目。
模32位索引-示例2
该环包含12536个条目。
注意:为了便于理解,我们在上述示例中使用65536模运算。在实际执行情况下,这对于降低效率是多余的,但是当结果溢出时会自动完成。
该代码始终将生产者和消费者之间的距离保持在0到size(ring)-1之间。由于具有此属性,我们可以在32位模的基础上进行2个索引值之间的减法:这就是为什么索引溢出不是问题。
在任何时候,即使只有第一项减法已溢出,entry和free_entries仍在0到size(ring)-1之间。
uint32_t item = (prod_tail - cons_head ); uint32_t free_entries = (mask + cons_tail - prod_head );
生产者/消费者同步模式
rte_ring为生产者和消费者支持不同的同步模式。这些模式可以在振铃创建/初始化时通过flags
参数指定。这应该可以帮助用户以最适合其特定使用场景的方式配置铃声。当前支持的模式:
MP / MC(默认一项)
多生产者(/多消费者)模式。这是环网的默认入队(/出队)模式。在此模式下,多个线程可以将对象排入(/出队)到环(/从环)。对于“经典” DPDK部署(每个内核只有一个线程),这通常是最合适,最快的同步模式。作为一个众所周知的局限性,它可以在某些过度使用的情况下执行纯粹的操作。
SP / SC
单生产者(/单消费者)模式。在此模式下,一次仅允许一个线程将对象排队(或从队列中取出)。
MP_RTS / MC_RTS
具有轻松尾部同步(RTS)模式的多生产者(/多消费者)。与原始MP / MC算法的主要区别在于,尾值不会由每个完成入队/出队的线程增加,而只会由最后一个线程增加。这样一来,线程就可以避免旋转环尾值,而将给定实例的实际尾值更改留给最后一个线程。该技术有助于避免在更新尾部时出现“锁定等待抢占”(LWP)问题,并改善了过量使用系统上的平均入队/出队时间。为了实现RTS,每个入队(/出队)操作需要2个64位CAS:一个用于头部更新,第二个用于尾部更新。相比之下,原始的MP / MC算法需要一个32位CAS来进行磁头更新和尾值的等待/旋转。
MP_HTS / MC_HTS
具有头/尾同步(HTS)模式的多生产者(/多消费者)。在这种模式下,入队/出队操作已完全序列化:在任何给定时刻,只能进行一次入队/出队操作。这是通过允许线程head.value
仅在时才进行更改来实现的。头部和尾部的值都自动更新(作为一个64位值)。为了实现这一点,头部更新例程将使用64位CAS。该技术还避免了在尾部更新时发生的“锁定等待抢占”(LWP)问题,并有助于在过量使用的情况下改善环入队/出队行为。完全序列化的生产者/消费者的另一个优点-它提供了为rte_ring实施MT安全查看API的能力。head.value == tail.value
环窥API
对于具有序列化生产者/消费者(HTS同步模式)的环,可以将公共入队/出队API分为两个阶段:
- 入队/出队开始
- 入队/出队完成
这样,用户就可以检查环中的对象而无需将其从环中移除(又名MT安全偷看),并在实际入队之前为环中的对象保留空间。请注意,此API仅适用于两种同步模式:
- 单一生产者/单一消费者(SP / SC)
- 具有头/尾同步(HTS)的多生产者/多消费者
用适当的同步模式创建/初始化振铃是用户的责任。作为用法示例:
/ *从环读取1个元素:* / uint32_t n = rte_ring_dequeue_bulk_start (ring , &obj , 1 , NULL ); if (n != 0 ) { / *检查对象* / if (object_examine (obj ) == KEEP )/ *决定将其保留在环中。* / rte_ring_dequeue_finish (ring , 0 ); 否则/ *决定将其从环中删除。* / rte_ring_dequeue_finish (铃声, n ); }
请注意,在_start_
和之间_finish_
没有其他线程可以继续进行enqueue(/ dequeue)操作,直到_finish_
完成。
参考资料
- FreeBSD(版本8)中的bufring.h
- FreeBSD(版本8)中的bufring.c
- Linux无锁环形缓冲区设计
相关文章
https://rtoax.blog.csdn.net/article/details/107086652
DPDK ring库:环形缓冲区的解剖相关推荐
- Ring Buffer (circular Buffer)环形缓冲区简介
https://blog.csdn.net/langeldep/article/details/8888582 关于环形缓冲区的知识,请看这里 http://en.wikipedia.org/wiki ...
- 环形缓冲区的实现原理(ring buffer)
消息队列锁调用太频繁的问题算是解决了,另一个让人有些苦恼的大概是这太多的内存分配和释放操作了.频繁的内存分配不但增加了系统开销,更使得内存碎片不断增多,非常不利于我们的服务器长期稳定运行.也许我们可以 ...
- SQL Server 环形缓冲区(Ring Buffer) -- 介绍
SQL Server 环形缓冲区(Ring Buffer) -- 介绍 以下关于Ring Buffer的介绍转载自: http://zh.wikipedia.org/wiki/%E7%92%B0%E5 ...
- SQL Server 环形缓冲区(Ring Buffer) -- 环形缓冲在AlwaysOn的应用
SQL Server 环形缓冲区(Ring Buffer) -- 环形缓冲在AlwaysOn的应用 可以从SQL Server环形缓冲区得到一些诊断AlwaysOn的信息,或从sys.dm_os_ri ...
- 环形缓冲区(Ring Buffer)使用说明
本说明涉及如下内容 什么是环形缓冲区 如何使用环形缓冲区 函数调用说明 环形缓冲区源码下载地址https://download.csdn.net/download/xm_smallp/86248489 ...
- 优秀的内存规划方法——环形缓冲区(ring buffer)
目录 什么是环形缓冲区 使用环形buffer的好处 环形buffer的使用场景 进程间通信 网络IO 区分缓冲区是满或者是空 计数 保持一个存储单元为空 镜像指示位 buffer满了之后的操作 实时流 ...
- 环形缓冲区实现(C语言)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 要求 一.环形缓冲区 二.环形缓冲区实现(C语言) 1. ringBuf.h 2. ringBufInit.c 3. rin ...
- 架构设计:生产者/消费者模式 第6页:环形缓冲区的实现
2019独角兽企业重金招聘Python工程师标准>>> ◇判断"空"和"满" 上述的操作并不复杂,不过有一个小小的麻烦:空环和满环的时候,R和 ...
- 环形缓冲区ringbuffer
环形缓冲区是生产者和消费者模型中常用的数据结构.生产者将数据放入数组的尾端,而消费者从数组的另一端移走数据,当达到数组的尾部时,生产者绕回到数组的头部. 如果只有一个生产者和一个消费者,那么就可以做到 ...
最新文章
- Kali Linux 安全渗透教程第四更1.3 Kali Linux简介
- redmine 一键安装
- JSValidation 配置文件
- 关于Qstring.replace传参Qstring.length为0引起程序退出的记录
- MySQL升级后 MySQL 5.7 时间不兼容问题
- Docker技术三大要点:cgroup, namespace和unionFS的理解
- LeetCode 366. 寻找二叉树的叶子节点(上下翻转二叉树+BFS)
- SpringBoot中使用Hibernate Validator校验工具类
- mysql 头行关联_mysql实现一样变多行(表关联,批量实现)
- 【调试手段】:printf统一为宏控制
- C#实现图像下一张上一张
- learn git 廖雪峰GIT教材1 创建与合并分支
- linux导入表dmp文件命令,linux下导入.dmp文件
- 2023计算机考研专业课参考书目(408)
- 演讲稿【物性的神奇】
- P1931 套利-SPFA最长路与环的判断
- SATA电源线的作用
- 币值最大化问题 C++C++
- PCL中点云特征描述与提取精通级实例解析
- python 打印三角形
热门文章
- maven添加jar包依赖
- 2019.7.25锻炼逻辑思维9道题。
- 团队第一阶段冲刺——第七天
- BJOI2018 简要题解
- Oracle总结第一篇【基本SQL操作】
- BZOJ:4820: [Sdoi2017]硬币游戏BZOJ:1444: [Jsoi2009]有趣的游戏(高斯消元求概率)
- SSH远程登录原理与运用
- HEAP: Free Heap block xxxxxxxx modified at xxxxxxxx after it was freed
- 最短路径之dijkstra算法的C语言实现
- Linux中让普通用户拥有超级用户的权限