我正在阅读有关RCU的内容。我不确定在SMP的情况下我是否理解正确。据我所知,RCU确保Update是自动执行的。以单链表为例,很明显,交换旧元素和新元素可以在一次操作中完成,因为它是通过改变指针来完成的。但是如何确保RCU在双链表的情况下仍然会自动执行呢?有两个指针指向给定的元素(next和prev),因此对该元素的每次更改都需要更改这两个指针。如何确保改变这两个指针将完成原子操作?在Linux中是如何实现的?
我也在问自己同样的问题,快速搜索得到了一条评论的回复,这条评论摘自Paul McKenney(据我所知,他是RCU思想背后的多个并发发明者之一)的一篇介绍文章。
问题:
我想知道在例子中省略反向链接是否是a件好事。自出版以来,这种遗漏使该技术变得微不足道只需要替换一个指针。
第二个,后面一个呢?不支持原子双指针更新,怎么能同时p->prev->next = q和p->next->prev = q更新在不冒客户端看到不一致视图的风险的情况下执行双链表?或者这在实践中没有问题吗?
不过还是谢谢你的文章。期待下一期!
答案:
很高兴你喜欢这篇文章,谢谢你的好问题!我可以给出很多答案,包括:在生产系统中,琐碎的技术是非常好的。给我举一个例子,在RCU保护下遍历->prev指针是有用的。给出几个这样的例子,我们可以找出如何最好地支持这一点。一致性被过分高估了。(不过,并不是每个人都同意我的观点!)即使使用原子双指针更新,也要考虑以下事件序列:(1)任务1执行p=p->next;(2)任务2在任务1刚刚处理的两个元素之间插入一个新元素;(3)任务1执行p=p->prev,但未能回到开始的位置!即使是双指针原子更新也无法消除不一致性!: -)如果需要一致性,请使用锁。在上面的例子中,我们可以通过简单地按顺序赋值指针来支持相当于双指针原子更新的一致性级别——例如,只需从list_del_rcu()中删除prev-pointer中毒。但是这样做会牺牲捕获指针中毒当前捕获的错误的能力。
所以,将来Linux内核可能会允许在rcu保护下在两个方向上遍历链表,但在实现它之前,我们需要看到对它的迫切需求。
所以基本上,Linux在做RCU时不允许在两个方向上反向遍历。正如评论中提到的,你可以使用一些较新的硬件机制,如双比较和交换,但它们不是无处不在,正如提到的,你仍然可以得到内存一致性问题。