DPDK Programmer’s Guide(5)Ring Library

相关Ring性能测试介绍链接如下:https://blog.csdn.net/NachtZ/article/details/72789820

官方文档查看地址:
http://doc.dpdk.org/guides/prog_guide/ring_lib.html
PDF下载地址:
https://www.intel.com/content/www/us/en/embedded/technology/packet-processing/dpdk/dpdk-programmers-guide.html

本篇难度系数:
翻译:★★★★☆
理解:★★★☆☆

5.环库
该环允许管理队列。rte_ring没有无限大小的链表,而是具有以下属性:

  • 先进先出
  • 最大大小是固定的,指针存储在一个表中
  • 无锁的实现
  • 多用户或单用户出队列
  • 多生产者或单生产者入队列
  • 批量出队列——如果成功,则退出指定的对象数量;否则失败
  • 批量入队列——如果成功,则对指定的对象计数进行排队;否则失败
  • 突发出队列——如果无法完成指定的计数,则删除最大可用对象
  • 突发入队列——如果无法完成指定的计数,则对最大可用对象排队

这种数据结构相对于链表队列的优点如下:

  • 快;只需要一个sizeof(void *)的比较和交换指令,而不是几个双比较和交换指令。
  • 比完整的无锁队列更简单。

适用于批量入/出队列操作。由于指针存储在表中,多个对象的dequeue不会产生与链接队列中一样多的缓存丢失。此外,许多对象的批量脱队列并不比简单对象的脱队列花费更多。

缺点:

  • 尺寸是固定的
  • 与链表队列相比,拥有多个环需要更多的内存。空环至少包含N个指针。

一个简单的环表示形式如下所示:消费者和生产者的头和尾指针指向存储在数据结构中的对象。

在这里插入图片描述
5.1在FreeBSD*中实现环的参考资料
以下代码是在FreeBSD 8.0中添加的,并在一些网络设备驱动程序中使用(至少在Intel驱动程序中):

  • 在FreeBSD bufring.h
  • 在FreeBSD bufring.c

5.2。Linux*中的无锁环缓冲区

下面是描述Linux无锁环缓冲区设计的链接。 Linux Lockless Ring Buffer Design http://lwn.net/Articles/340400/

5.3附加功能

5.3.1名字

一个环由一个唯一的名字来标识。不可能创建具有相同名称的两个环(如果尝试这样做,rte_ring_create()将返回NULL)。

5.4用例

Ring库的用例包括:

  • DPDK中的应用程序之间的通信
  • 内存池分配器使用

5.5环缓冲器的解剖

本节解释循环缓冲区的工作方式。环结构由两对头尾对组成;一个供生产者使用,一个供消费者使用。以下部分的图将它们称为prod_head、prod_tail、cons_head和cons_tail。

每个图表示一个简化的环形状态,即一个循环缓冲区。函数局部变量的内容在图的顶部表示,环结构的内容在图的底部表示。

5.5.1单一生产者入队

本节解释当生产者向环中添加对象时会发生什么。在本例中,只修改了生产者head和tail (prod_head和prod_tail),并且只有一个生产者。

初始状态是使prod_head和prod_tail指向相同的位置。

5.5.1.1入队的第一步

首先,在局部变量中复制ring->prod_head和ring->cons_tail。prod_next局部变量指向表的下一个元素,或者在批量排队时指向表后的几个元素。如果环中没有足够的空间(通过检查cons_tail可以检测到),它将返回一个错误。

在这里插入图片描述
5.5.1.2入队第二步

第二步是修改环结构中的ring->prod_head,使其指向与prod_next相同的位置。

一个指向添加对象的指针被复制到环中(obj4)

在这里插入图片描述
5.5.2单个消费者出列

本节解释当使用者将对象从环中取出时会发生什么。在本例中,只修改了使用者head和tail (cons_head和cons_tail),并且只有一个使用者。

初始状态是cons_head和cons_tail指向同一个位置。

5.5.2.1出列的第一步

首先,在局部变量中复制ring->cons_head和ring->prod_tail。cons_next局部变量指向表的下一个元素,或者在批量脱队列的情况下指向多个元素。如果环中没有足够的对象(通过检查prod_tail来检测),则返回一个错误。
在这里插入图片描述
5.5.2.2出列第二步

第二步是修改环结构中的ring->cons_head,使其指向与cons_next相同的位置。

指向dequeued对象(obj1)的指针被复制到用户给出的指针中。
在这里插入图片描述
5.5.2.3出列最后一步

最后,将环结构中的ring->cons_tail修改为指向与ring->cons_head相同的位置。退出队列操作已经完成。
在这里插入图片描述
5.5.3多个生产者排队

本节解释当两个生产者同时向环中添加一个对象时会发生什么。在本例中,只修改了生产者head和tail (prod_head和prod_tail)。

初始状态是使prod_head和prod_tail指向相同的位置。

5.5.3.1第一步是多个生产者排队

在两个核心上,ring->prod_head和ring->cons_tail都被复制到局部变量中。prod_next局部变量指向表的下一个元素,或者在批量排队的情况下指向后几个元素。

如果环中没有足够的空间(通过检查cons_tail可以检测到),它将返回一个错误。
在这里插入图片描述
5.5.3.2多个生产者进入第二步

第二步是修改环结构中的ring->prod_head,使其指向与prod_next相同的位置。这个操作是使用比较和交换(CAS)指令完成的,它原子性地执行以下操作:

  • 如果ring->prod_head与本地变量prod_head不同,则CAS操作失败,代码在第一步重新启动。
  • 否则,将ring->prod_head设置为local prod_next, CAS操作成功,处理继续。

在图中,操作在core 1上成功,步骤1在core 2上重新启动。
在这里插入图片描述
5.5.3.3多个生产者进入第三步

CAS操作在core 2上重试成功。核心1更新环的一个元素(obj4),核心2更新另一个元素(obj5)。
在这里插入图片描述
5.5.3.4多个生产者排队进入第四步

现在每个核心都想要更新ring->prod_tail。核心只能在ring->prod_tail等于prod_head局部变量时更新它。这只适用于core 1。核心1上的操作已经完成。
在这里插入图片描述
5.5.3.5最后一步是多个生产者排队

一旦core 1更新了ring->prod_tail, core 2也可以更新它。操作也在core 2上完成。
在这里插入图片描述
5.5.4模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位整数。
在这里插入图片描述
请注意
为了便于理解,我们在上面的示例中使用了模65536操作。在实际的执行案例中,这对于低效率来说是冗余的,但是当结果溢出时就会自动执行。

代码始终在生产者和消费者之间保持0和size(ring)-1之间的距离。由于这个属性,我们可以在一个32位基中对两个索引值进行减法:这就是索引溢出不是问题的原因。

在任何时候,条目和free_entries都在0和size(ring)-1之间,即使只有减法的第一项溢出:

uint32_t entries = (prod_tail - cons_head);
uint32_t free_entries = (mask + cons_tail -prod_head);

5.6 参考文献

bufring.h in FreeBSD (version 8) https://svnweb.freebsd.org/base/release/8.0.0/sys/sys/buf_ring.h?revision=199625&view=markup
bufring.c in FreeBSD (version 8) https://svnweb.freebsd.org/base/release/8.0.0/sys/kern/subr_bufring.c?revision=199625&view=markup
Linux Lockless Ring Buffer Design https://lwn.net/Articles/340400/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章