聊一聊无锁队列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 ...
随机推荐
- TTL电平,CMOS电平,232/485电平,OC门,OD门基础知识
1.RS232电平 或者说串口电平,有的甚至说计算机电平,所有的这些说法,指得都是计算机9针串口 (RS232)的电平,采用负逻辑, -15v ~ -3v 代表1 +3v ~ +15v 代表0 2. ...
- ReSharper 注册码
用户名:ronle 注册码:ZoJzmeVBoAv9Sskw76emgksMMFiLn4NM 原文地址:http://hi.baidu.com/ronle/item/a509b5f7b851971be ...
- 第四章 NFS服务相关介绍
一.NFS服务介绍 1.什么是NFS?是一个共享存储,文件服务器 2.NFS基本概述NFS是Network File System的缩写及网络文件系统.NFS主要功能是通过局域网络让不同的主机系统之间 ...
- 第三十二章 Linux常规练习题(一)
一.练习题一 1.超级用户(管理员用户)提示符是____,普通用户提示符是____.2.linux关机重启的命令有哪些 ?3.bash是什么?4.bash特性, 常见的bash特性有哪些?5.网卡的配 ...
- Python常用模块之random和time
常用模块: time: 分为三种格式: 1.时间戳:从1970年1月1日0点0分0秒到现在经过的秒数 用于时间间隔的计算 import time print(time.time()) 2.字符串显示时 ...
- Python列表的增删改查
列表的增: li = ['libai','sushi','dufu','sushi',"白居易"] 第一种: append():向列表末尾追加元素 li.append('diaoc ...
- [斯坦福大学2014机器学习教程笔记]第六章-代价函数(Cost function)
在这节中主要讲的是如何更好地拟合逻辑回归模型的参数θ.具体来说,要定义用来拟合参数的优化目标或者叫代价函数,这便是监督学习问题中的逻辑回归模型的拟合问题. 我们有一个训练集,训练集中有m个训练样本:{ ...
- 微服务nacos服务注册与发现
一,以上一篇为基础 微服务从nacos配置中心获得配置信息 给service1, service2添加依赖 <dependency> <groupId>com.alibaba. ...
- Lombda表达式(四)
/* * 自定义函数式接口: * 1.声明一个接口,只能包含一个抽象方法 * 2.给这个接口加@FunctionalInterface */ public class Test { public st ...
- new 正则
在做项目的过程中,使用正则表达式来匹配一段文本中的特定种类字符,是比较常用的一种方式,下面是对常用的正则匹配做了一个归纳整理. 匹配中文:[\u4e00-\u9fa5] 英文字母:[a-zA-Z]数字 ...