rcu_read_lock和 x86-64 内存排序



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

对于其他 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 避免了读取路径中完整内存屏障的开销。这是它的主要吸引力之一,除了避免争论。

相关内容

  • 没有找到相关文章

最新更新