之前用基于dpdk 实现小包快速转发的时候有用到无锁队列!今天就来看看吧!(后续完成了去dpdk化,直接在内核完成快速转发功能)

dpdk的无锁队列ring是借鉴了linux内核kfifo无锁队列。ring的实质是FIFO的环形队列。

  • 先进先出(FIFO)
  • 最大大小固定,指针存储在表中
  • 无锁实现
  • 多消费者或单消费者出队操作
  • 多生产者或单生产者入队操作
  • 批量出队 - 如果成功,将指定数量的元素出队,否则什么也不做
  • 批量入队 - 如果成功,将指定数量的元素入队,否则什么也不做
  • 突发出队 - 如果指定的数目出队失败,则将最大可用数目对象出队
  • 突发入队 - 如果指定的数目入队失败,则将最大可入队数目对象入队

相比于链表,这个数据结构的优点如下:

  • 更快;只需要一个sizeof(void *)的Compare-And-Swap指令,而不是多个双重比较和交换指令
  • 与完全无锁队列像是
  • 适应批量入队/出队操作。 因为指针是存储在表中的,应i多个对象的出队将不会产生于链表队列中一样多的cache miss。 此外,批量出队成本并不比单个对象出队高。

缺点:

  • 大小固定
  • 大量ring相比于链表,消耗更多的内存,空ring至少包含n个指针。
/* structure to hold a pair of head/tail values and other metadata */
struct rte_ring_headtail {
// 生产者头尾指针,生产完成后都指向队尾
// 消费者头尾指针,生产完成后都指向队头
volatile uint32_t head; /**< Prod/consumer head.预生产到地方/预出队的地方 */
volatile uint32_t tail; /**< Prod/consumer tail. 实际生产了的数量 /实际出队的地方 */
uint32_t single; /**< True if single prod/cons */
};
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 */
};

入队列:

http://reader.epubee.com/books/mobile/54/54aa973816d258a932e39464018932ee/text00032.html  以上来自~~~~~~~~~~~~~~

static __rte_always_inline unsigned int
__rte_ring_do_enqueue(struct rte_ring *r, void * const *obj_table,
unsigned int n, enum rte_ring_queue_behavior behavior,
unsigned int is_sp, unsigned int *free_space)
{
uint32_t prod_head, prod_next;
uint32_t free_entries; n = __rte_ring_move_prod_head(r, is_sp, n, behavior,
&prod_head, &prod_next, &free_entries);
if (n == 0)
goto end;
//prod_head是旧的r->prod.head
//r经过__rte_ring_move_prod_head处理后,r->prod.head已经移动到想要的位置&r[1]是数据的位置
ENQUEUE_PTRS(r, &r[1], prod_head, obj_table, n, void *); update_tail(&r->prod, prod_head, prod_next, is_sp, 1);
end:
if (free_space != NULL)
*free_space = free_entries - n;
return n;
} static __rte_always_inline unsigned int
__rte_ring_move_prod_head(struct rte_ring *r, unsigned int is_sp,
unsigned int n, enum rte_ring_queue_behavior behavior,
uint32_t *old_head, uint32_t *new_head,
uint32_t *free_entries)
{
const uint32_t capacity = r->capacity;
unsigned int max = n;
int success; do {
/* Reset n to the initial burst count */
n = max; *old_head = r->prod.head; /* add rmb barrier to avoid load/load reorder in weak
* memory model. It is noop on x86
*/
rte_smp_rmb(); /*
* The subtraction is done between two unsigned 32bits value
* (the result is always modulo 32 bits even if we have
* *old_head > cons_tail). So 'free_entries' is always between 0
* and capacity (which is < size).
计算当前可用容量,
cons.tail是小于等于prod.head, 所以r->cons.tail - *old_head得到一个
负数,capacity减这个差值就得到剩余的容量
*/
*free_entries = (capacity + r->cons.tail - *old_head); /* check that we have enough room in ring */
if (unlikely(n > *free_entries))
n = (behavior == RTE_RING_QUEUE_FIXED) ?
0 : *free_entries; if (n == 0)
return 0; *new_head = *old_head + n; /* 新头的位置 */
if (is_sp) {/* 如果是单生产者,直接更新r->prod.head即可,不需要加锁 */
r->prod.head = *new_head, success = 1;
}else{
/* 如果是多生产者,需要使用cmpset比较,如果&r->prod.head == *old_head
则&r->prod.head = *new_head
否则重新循环,获取新的*old_head = r->prod.head,知道成功位置*/
success = rte_atomic32_cmpset(&r->prod.head, *old_head, *new_head);
}
} while (unlikely(success == 0));
return n;
}

出队:

原理逻辑和入队一样 代码也比较相似,不具体分析

static __rte_always_inline unsigned int
__rte_ring_do_dequeue(struct rte_ring *r, void **obj_table,
unsigned int n, enum rte_ring_queue_behavior behavior,
unsigned int is_sc, unsigned int *available)
{
uint32_t cons_head, cons_next;
uint32_t entries; n = __rte_ring_move_cons_head(r, (int)is_sc, n, behavior,
&cons_head, &cons_next, &entries);
if (n == 0)
goto end; DEQUEUE_PTRS(r, &r[1], cons_head, obj_table, n, void *); update_tail(&r->cons, cons_head, cons_next, is_sc, 0); end:
if (available != NULL)
*available = entries - n;
return n;
} static __rte_always_inline unsigned int
__rte_ring_move_cons_head(struct rte_ring *r, unsigned int is_sc,
unsigned int n, enum rte_ring_queue_behavior behavior,
uint32_t *old_head, uint32_t *new_head,
uint32_t *entries)
{
unsigned int max = n;
int success; /* move cons.head atomically */
do {
/* Restore n as it may change every loop */
n = max; *old_head = r->cons.head; /* add rmb barrier to avoid load/load reorder in weak
* memory model. It is noop on x86
*/
rte_smp_rmb(); /* The subtraction is done between two unsigned 32bits value
* (the result is always modulo 32 bits even if we have
* cons_head > prod_tail). So 'entries' is always between 0
* and size(ring)-1.
*/
*entries = (r->prod.tail - *old_head); /* Set the actual entries for dequeue */
if (n > *entries)
n = (behavior == RTE_RING_QUEUE_FIXED) ? 0 : *entries; if (unlikely(n == 0))
return 0; *new_head = *old_head + n;
if (is_sc)
r->cons.head = *new_head, success = 1;
else
success = rte_atomic32_cmpset(&r->cons.head, *old_head,
*new_head);
} while (unlikely(success == 0));
return n;
}

 

聊一聊无锁队列rte_ring的更多相关文章

  1. 【DPDK】【ring】从DPDK的ring来看无锁队列的实现

    [前言] 队列是众多数据结构中最常见的一种之一.曾经有人和我说过这么一句话,叫做“程序等于数据结构+算法”.因此在设计模块.写代码时,队列常常作为一个很常见的结构出现在模块设计中.DPDK不仅是一个加 ...

  2. 无锁队列以及ABA问题

    队列是我们非常常用的数据结构,用来提供数据的写入和读取功能,而且通常在不同线程之间作为数据通信的桥梁.不过在将无锁队列的算法之前,需要先了解一下CAS(compare and swap)的原理.由于多 ...

  3. HashMap的原理与实 无锁队列的实现Java HashMap的死循环 red black tree

    http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html https://zh.wikipedia.org/wiki/%E7%BA ...

  4. zeromq源码分析笔记之无锁队列ypipe_t(3)

    在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令.而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以 ...

  5. boost 无锁队列

    一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...

  6. 一个可无限伸缩且无ABA问题的无锁队列

    关于无锁队列,详细的介绍请参考陈硕先生的<无锁队列的实现>一文.然进一步,如何实现一个不限node数目即能够无限伸缩的无锁队列,即是本文的要旨. 无锁队列有两种实现形式,分别是数组与链表. ...

  7. 无锁队列--基于linuxkfifo实现

    一直想写一个无锁队列,为了提高项目的背景效率. 有机会看到linux核心kfifo.h 原则. 所以这个实现自己仿照,眼下linux我们应该能够提供外部接口. #ifndef _NO_LOCK_QUE ...

  8. CAS简介和无锁队列的实现

    Q:CAS的实现 A:gcc提供了两个函数 bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)// ...

  9. Go语言无锁队列组件的实现 (chan/interface/select)

    1. 背景 go代码中要实现异步很简单,go funcName(). 但是进程需要控制协程数量在合理范围内,对应大批量任务可以使用"协程池 + 无锁队列"实现. 2. golang ...

随机推荐

  1. js 为什么0.1+0.2不等于0.3

    当程序员在使用浮点数进行计算逻辑处理时,不注意,就可能出现问题, 记住,永远不要直接比较俩个浮点的大小 这个属于数字运算中的精度缺失的问题 在0.1 + 0.2这个式子中,0.1和0.2都是近似表示的 ...

  2. 为什么在M3架构中 PC总是返回加4

    由于CPU是3级流水线的方式运行.在执行第一条指令时候,已经对第二条指令译码,对第三条指令取值. PC总是指向正在取值的指令.由于在M3架构中,采用Thumb-2指令,每个指令占据2个字节,所以PC总 ...

  3. MeteoInfoLab脚本示例:多Y轴图

    数据范围相差比较大的数据序列进行对比的时候多Y轴图就很重要了.MeteoInfoLab中提供了一个twinx函数来根据已有的坐标系(Axes)生成一个新的Axes,这个命令会使得已有的Axes不绘制右 ...

  4. 震惊!OI居然还考天体运动

    看图说话 看这里: 标签: 标签竟然还是模拟,简直活到爆,物理老师狂喜

  5. SQL Server Management Studio (SSMS)单独安装,仅安装连接工具

    简单来说,SSMS是用于远程连接数据库与执行管理任务的一个工具.当安装SQL SERVER时,会默认安装.但也可以单独安装在不是数据库服务器的主机上. SQL Server Management St ...

  6. jquery $.ajax 获取josn数据

    <script type="text/javascript" src="jquery-1.9.1.js"></script> <s ...

  7. CSS实现鼠标移入弹出下拉框

    前言 最近比较沉迷CSS,所以我现在来做个鼠标的交互效果 HTML <ul> <li>测试</li> <li>测试</li> <li ...

  8. readcf: option RunAsUser: unknown user smmsp发送邮件失败问题

    今天使用mail命令发送邮件时,发送不了,错误信息如下: /etc/mail/submit.cf: line 432: readcf: option RunAsUser: unknown user s ...

  9. 学python,大概要多久?

    都让开!本人文科生,自学Python 2年半,作为一个曾经完全0基础,啥都不懂纯靠自学学会python的文科生,有一些不成熟的小建议可以分享一下. 首先不要觉着编程难,只要你认识26个英文字母,有一点 ...

  10. win10使用U盘安装Linux系统教程

    win10安装Linux系统详细教程 目前想要再Windows系统上安装Linux系统有三种方式:其一是安装在虚拟机上(VMWare或者VirtualBox),其二是使用win10最新支持的Linux ...