1. DPDK Ring简介
dpdk实现了一个无锁环形队列Ring,可用于在dpdk不同的应用程序之间进行通信。
逻辑上ring看起来如下图:
在使用这个结构的时候,一般是将1个核作为生产者,向这个ring队列里面添加数据;另一个core或者多个core 作为消费者从这个ring队列中获取数据。
生产者核访问上面的prod结构,消费者访问cons结构。
Ring支持的效果:
- 先进先出
- 最大大小是固定的,指针存储在表中
- 无锁实现
- 多消费者或单消费者出队
- 多生产者或单生产者入队
struct rte_ring {
/* Ring producer status. */
struct prod {
uint32_t watermark; /**< Maximum itemsbefore EDQUOT. */
uint32_t sp_enqueue; /**< True, if single producer. */
uint32_t size; /**< Size of ring.*/
uint32_t mask; /**< Mask (size-1)of ring. */
volatile uint32_thead; /**< Producer head.*/
volatileuint32_t tail; /**< Producer tail.*/
} prod __rte_cache_aligned;
/* Ring consumer status. */
struct cons {
uint32_t sc_dequeue; /**< True, if single consumer. */
uint32_t size; /**< Size of thering. */
uint32_t mask; /**< Mask (size-1)of ring. */
volatileuint32_t head; /**< Consumer head.*/
volatileuint32_t tail; /**< Consumer tail.*/
} cons __rte_cache_aligned;
void*ring[] __rte_cache_aligned;
};
2. Ring实现
2.1 单一生产者入队
首先,将ring-> prod_head和ring-> cons_tail复制到局部变量中。prod_next局部变量指向表的下一个元素,或在批量入队的情况下指向多个元素。
如果环中没有足够的空间(通过检查cons_tail可以检测到),它将返回错误。
第二步是修改环结构中的ring-> prod_head以指向与prod_next相同的位置。
指向添加对象的指针将复制到环(obj4)中。
将对象添加到环中后,将修改环结构中的ring-> prod_tail使其指向与ring-> prod_head相同的位置。入队操作完成。
2.2 单一消费者出队
同上
2.3 多个生产者入队
在两个CPU core上,ring-> prod_head和ring-> cons_tail都复制到局部变量中
。prod_next局部变量指向表的下一个元素,或者在批量入队的情况下指向多个元素。
如果环中没有足够的空间(通过检查cons_tail 与 prod_head 差值可以检测到)
第二步是修改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上完成。
2.4 多个消费者出队
同上
3 总结
3.1 入队列
- 抢先更新头指针,此时头指针被更新为下一次入队列的起始位置,此时另外一个进程也是可以同时入队列的,只是要排队修改尾指针
- 然后放入数据
- 然后更新尾指针,其他进程排队更新尾指针
3.2 出队列
- 抢先更新头指针,此时头指针被更新为下一次出队列的起始位置,此时另外一个进程也是可以同时出队列的,只是要排队修改尾指针
- 然后取出数据
- 然后更新尾指针,其他进程排队更新尾指针
参考:https://blog.csdn.net/gengzhikui1992/article/details/108202876