rcu_read_lock和x86-64内存排序

问题描述 投票:1回答:1

在可抢占的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,导致它与另一个线程中运行的回收代码同时运行?

linux x86 memory-barriers rcu
1个回答
2
投票

对于其他CPU上的观察者,没有什么能阻止这种情况。你没错,StoreLoad重新排序++的商店部分可以让你在一些负载之后全局可见。

因此,我们可以得出结论,current->rcu_read_lock_nesting只能通过在此核心上运行的代码观察到,或者通过在此处调度来远程触发此核心上的内存屏障,或者使用专用机制来使所有核心在处理程序中执行障碍处理器间中断(IPI)。例如类似于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避免了读路径中完整内存屏障的开销。除了避免争用之外,这是它的主要吸引力之一。

© www.soinside.com 2019 - 2024. All rights reserved.