聊一聊无锁队列rte_ring
之前用基于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的更多相关文章
- 【DPDK】【ring】从DPDK的ring来看无锁队列的实现
		
[前言] 队列是众多数据结构中最常见的一种之一.曾经有人和我说过这么一句话,叫做“程序等于数据结构+算法”.因此在设计模块.写代码时,队列常常作为一个很常见的结构出现在模块设计中.DPDK不仅是一个加 ...
 - 无锁队列以及ABA问题
		
队列是我们非常常用的数据结构,用来提供数据的写入和读取功能,而且通常在不同线程之间作为数据通信的桥梁.不过在将无锁队列的算法之前,需要先了解一下CAS(compare and swap)的原理.由于多 ...
 - HashMap的原理与实  无锁队列的实现Java HashMap的死循环 red black tree
		
http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html https://zh.wikipedia.org/wiki/%E7%BA ...
 - zeromq源码分析笔记之无锁队列ypipe_t(3)
		
在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令.而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以 ...
 - boost  无锁队列
		
一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...
 - 一个可无限伸缩且无ABA问题的无锁队列
		
关于无锁队列,详细的介绍请参考陈硕先生的<无锁队列的实现>一文.然进一步,如何实现一个不限node数目即能够无限伸缩的无锁队列,即是本文的要旨. 无锁队列有两种实现形式,分别是数组与链表. ...
 - 无锁队列--基于linuxkfifo实现
		
一直想写一个无锁队列,为了提高项目的背景效率. 有机会看到linux核心kfifo.h 原则. 所以这个实现自己仿照,眼下linux我们应该能够提供外部接口. #ifndef _NO_LOCK_QUE ...
 - CAS简介和无锁队列的实现
		
Q:CAS的实现 A:gcc提供了两个函数 bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)// ...
 - Go语言无锁队列组件的实现 (chan/interface/select)
		
1. 背景 go代码中要实现异步很简单,go funcName(). 但是进程需要控制协程数量在合理范围内,对应大批量任务可以使用"协程池 + 无锁队列"实现. 2. golang ...
 
随机推荐
- day67:Vue:es6基本语法&vue.js基本使用&vue指令系统
			
目录 Vue前戏:es6的基本语法 1.es6中的let特点 1.1.局部作用域 1.2.不存在变量提升 1.3.不能重复声明 1.4.let声明的全局变量不从属于window对象,var声明的全局变 ...
 - IIdea使用CXF开发WebService
			
写这篇文章主要是用于增强记忆,而我参考的是这位朋友的随笔,链接如下 http://www.xiaomager.com/415.html 服务端开发过程 1.首先创建一个maven项目,如下图 2.添加 ...
 - Mysql架构与内部模块-第二章
			
接上文,上文简述到了Mysql中的查询缓存和解析器,今日我们继续. 先来看一段SQL:SELECT * FROM `jianghuadong`; 先假设我们数据库中并没有一张名为jianghuadon ...
 - pytest文档55-plugins插件开发
			
前言 前面一篇已经学会了使用hook函数改变pytest运行的结果,代码写在conftest.py文件,实际上就是本地的插件了. 当有一天你公司的小伙伴觉得你写的还不错,或者更多的小伙伴想要你这个功能 ...
 - 【换根DP】小奇的仓库
			
题目背景 小奇采的矿实在太多了,它准备在喵星系建个矿石仓库.令它无语的是,喵星系的货运飞船引擎还停留在上元时代! 题目内容 喵星系有\(n\)个星球,星球以及星球间的航线形成一棵树. 从星球\(a\) ...
 - spring boot:多模块项目生成jar包(spring boot 2.3.3)
			
一,多模块项目的优点: 1,为什么要使用多模块项目? 相比传统的单体工程,使用Maven的多模块配置, 有如下优点: 帮助项目划分模块,鼓励重用, 防止POM变得过于庞大, 方便某个模块的构建,而不用 ...
 - gin+gorm 用户服务
			
package main import ( "fmt" "github.com/gin-gonic/gin" "github.com/jinzhu/g ...
 - Seaborn中几种作图方式
			
趋势 sns.pointplot - 点图 ,比折线图好使 sns.lineplot - 折线图最适合显示一段时间内的趋势,多条线可以用来显示多个组中的趋势. 关系 - 可以使用许多不同的图表类型来理 ...
 - STL: set和map的区别、联系、使用
			
set是一种关联式容器,其特性如下: set以RBTree作为底层容器 所得元素的只有key(键)没有value(值) 不允许出现键重复 所有的元素都会被自动排序 不能通过迭代器来改变set的值,因为 ...
 - linux环境下protobuf安装
			
1. 到GitHub下载源码,执行解压命令后,进入解压后的目录 2. 执行./autogen,生成configure 3. 执行./configure --prefix=/usr/local/,pro ...