免责声明:亲爱的网友,虽然我已尽力去还原本文所讲内容的真实情况,但囿于水平不够,以下内容可能全是错的,不过还是希望对你有所启发。
最近被问起一个问题:dpdk无锁队列是怎么实现的?如何能支持多消费者、多生产者?CAS内部的实现原理是什么?
这几个问题彼此关联,层层递进。想要回答清楚,还真不容易。看了一些资料后,说说我对这几个问题的理解。
1、不管是多生产者写入,还是多消费者读取,在dpdk的实现中,都会经过以下三个步骤:
- 先偏移头指针,说白了就是抢位置,这步主要是为了应对多生产者和多消费者的情况
- 抢到位置后写入数据或者读取数据
- 写入或读取完毕后,更新尾指针,让消费者可以消费刚写入的数据,或者让生产者可以写入刚消费过的空间
2、不管是入队列,还是出队列,都是抢先改变头节点的位置,
假设是入队列,则
- 抢先更新头指针,此时头指针被更新为下一次入队列的起始位置,此时另外一个线程也是可以同时入队列的,只是要排队修改尾指针
- 然后放入数据
- 然后更新尾指针,其他线程排队更新尾指针
假设是出队列,则
- 抢先更新头指针,此时头指针被更新为下一次出队列的起始位置,此时另外一个线程也是可以同时出队列的,只是要排队修改尾指针
- 然后取出数据
- 然后更新尾指针,其他线程排队更新尾指针
3、CAS: 是一个原子操作,由cpu的指令集提供该功能,它需要处理器锁住它的指令流水线来保证原子性。
实现上的考虑:
为了实现多生产者同时入队列,或多消费者同时出队列,需要为队列维护好以下四个变量:prod_head(生产者头指针)、prod_tail(生产者尾指针)、cons_head(消费者头指针)、cons_tail(消费者尾指针),前两个用于多生产者入队列,后两个用于多消费者出队列。
维护的基本流程是:
1)抢占式更新 prod_head(或 cons_head),抢占通过 CAS 指令完成,抢占失败的线程通过 while 循环再次尝试抢占
2)入队列(或出队列),也即数据拷贝
3)按抢占成功的顺序依次排队更新 prod_tail(或 cons_tail)
从一个较长的时间线来看,在多生产者入队列开始前,prod_head 是等于 prod_tail 的,在多生产者入队列结束后,prod_head 还是等于 prod_tail 的,这是一个非常重要的约束。在多生产者入队列中,prod_head 和 prod_tail 会出现不相等的情况,这是一个从相等到不相等,再到相等的动态变化过程。在这个过程中,某个线程只有先成功更新 prod_head,才能安全的入队列,入队列结束后,要依次更新 prod_tail,最后的结果就是 prod_head 和 prod_tail 又重新相等。
参考资料
1、【DPDK】【ring】从DPDK的ring来看x86无锁队列的实现 - Jungle1996 - 博客园