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'。当前的生产者这时可以在不加锁的情况下将内容写入环中,并不和任意的其他生产者和消费者产生竞争和冲突。

  • 通过使用双生指针,解决了生产者和消费者的冲突问题。
  • 通过使用原子操作来进行隐性的加锁,解决了多个生产者或者多个消费者之间的竞争问题。

对于消费者其实道理是一样的,也是通过双消费者指针和对con_head进行原子操作来进行保护的。

这里其实引入了另外一个问题,那就是在多核情况下,如果通过原子指令来保证多核间的数据一致性呢?也就是说原子操作,compareandswap在多核场景下是如何实现的呢?将在下一篇博客中对这一概念进行介绍。

完成于2019-08-04

你可能感兴趣的:(DPDK lockless ring 介绍)