DPDK|DPDK lockless ring 介绍
DPDK中,通过ring结构来传递报文描述符,使用lockless ring来提高效率。
说是无锁ring,其实在实现中还是使用了锁的机制,只不过不是利用mutex等系统调用,而是通过原子操作来实现互斥的,具体是如何实现的呢,本文将对于DPDK中的实现细节进行介绍。
在了解实现细节前,先来分析一下无锁环需要解决一些什么样的问题。
一般情况下,对于临界区或者竞争资源,会采用加锁的方法来进行互斥或者保护,比如在更新ring的头尾指针时,需要进行加锁,更新完头尾指针并将内容写入环后,再释放锁。对于多消费者,多生产者的场景,需要在更新头尾指针前就行进加锁,将内容写入环中,更新完指针,再释放锁。加锁的时间是比较长的,如果ring的出入队操作比较频繁,那这个加锁的整体开销就会很大。DPDK中的lockless ring利用了三个要素来实现竞争资源的保护,分别是原子操作(compareandswap),生产者头尾指针和消费者头尾指针来将加锁的时间降到最低,并且不需要调用者显示的进行任何加锁操作。
为什么生产者需要两个指针呢?在之前的分析中,如果只有一个生产者的指针,那么就需要将入环的动作保护起来,这才能保证消费者和生产者之前的同步。否则如果更新了生产者指针,但是内容还没有写入环中,就会导致消费者读到脏数据。在DPDK中引入两个生产者指针,prod_tail和prod_head,当更新指针时,只更新prod_head,当数据写完后,再更新prod_tail,消费者只根据prod_tail的指针来读取数据,这样就解决了单生产者指针的问题。那么如何保证多个生产者之间的互斥问题呢?一般情况下,我们可以使用mutex对生产者的头指针进行保护,确保在任一时刻只有一个生产者可以对prod_head进行操作来进行互斥。在DPDK中,利用原子指令锁内存总线的方式实现了多个生产者的互斥。所使用的原子指令有三个操作数,分别是原来的prod_head(old),进行原子操作时的prod_head,写入新的数据后的prod_head‘ = prod_head(old) + n,原子指令会先对prod_head(old)和prod_head进行比较,如果相等,则证明在这个时间内没有其他生产者对prod_head进行操作,那么就会将prod_head'写入prod_head指针,相当于为当前的生产者预留了n个空间,从head_prod(old)到prod_head'。当前的生产者这时可以在不加锁的情况下将内容写入环中,并不和任意的其他生产者和消费者产生竞争和冲突。
- 通过使用双生指针,解决了生产者和消费者的冲突问题。
- 通过使用原子操作来进行隐性的加锁,解决了多个生产者或者多个消费者之间的竞争问题。
这里其实引入了另外一个问题,那就是在多核情况下,如果通过原子指令来保证多核间的数据一致性呢?也就是说原子操作,compareandswap在多核场景下是如何实现的呢?将在下一篇博客中对这一概念进行介绍。
【DPDK|DPDK lockless ring 介绍】完成于2019-08-04
推荐阅读
- Activiti(一)SpringBoot2集成Activiti6
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- 2018-07-09|2018-07-09 Spring 的DBCP,c3p0
- spring|spring boot项目启动websocket
- jvm关于String
- LeetCode(03)Longest|LeetCode(03)Longest Substring Without Repeating Characters
- Spring|Spring Boot 整合 Activiti6.0.0
- Spring集成|Spring集成 Mina
- springboot使用redis缓存