在可抢占的SMP内核上,rcu_read_lock编译以下内容:
current->rcu_read_lock_nesting++;
barrier();barrier是一个编译器指令,编译为零。
因此,根据英特尔的X86-64内存订购白皮书:
货物可能会与旧的商店重新排序到不同的地点。
为什么实现实际上是可以的?
考虑以下情况:
rcu_read_lock();
read_non_atomic_stuff();
rcu_read_unlock();是什么阻止read_non_atomic_stuff“泄漏”过去的rcu_read_lock,导致它与在另一个线程中运行的回收代码同时运行?
发布于 2019-04-09 03:52:54
对于其他CPU上的观察者来说,没有什么能阻止这一点。没错,StoreLoad对++存储部分的重新排序可以使其在一些负载之后在全局可见。
因此,我们可以得出这样的结论:只有运行在这个核心上的代码才能观察到current->rcu_read_lock_nesting ,或者是在这个核心上通过调度来远程触发内存障碍,或者使用一个专门的机制让所有内核在处理器间中断的处理程序中执行一个屏障。类似于membarrier()的用户空间系统调用。
如果该核心开始运行另一个任务,则该任务将确保按程序顺序查看该任务的操作。(因为它位于同一个核心上,而一个核心总是按照自己的操作顺序排列。)另外,上下文切换可能涉及到一个完整的内存屏障,这样任务就可以在另一个核心上恢复,而不会破坏单线程逻辑。(当这个任务/线程不在任何地方运行时,任何内核都可以安全地查看rcu_read_lock_nesting。)
注意到内核在机器的每个内核上启动一个RCU任务;例如,ps输出显示了4c8t四核上的[rcuc/0]、[rcuc/1]、.、[rcu/7]。想必他们是这个设计的一个重要组成部分,让读者没有障碍的等待。
我还没有详细了解RCU的所有细节,但是https://www.kernel.org/doc/Documentation/RCU/whatisRCU.txt中的“玩具”示例之一是“经典RCU”,它将synchronize_rcu()作为for_each_possible_cpu(cpu) run_on(cpu);来实现,以便在可能执行RCU操作(即每个内核)的每个核心上执行回收。一旦完成了,我们就知道一定有一个完整的记忆屏障发生在那里的某个地方,作为转换的一部分。
所以是的,RCU不遵循传统的方法,您需要一个完整的内存屏障(包括StoreLoad)来让核心等待,直到第一个存储在进行任何读取之前都是可见的。RCU避免了读取路径中的完全内存屏障的开销。是它的主要优点之一,除了避免争用之外。
https://stackoverflow.com/questions/55583472
复制相似问题