在可抢占式SMP内核上,data-info
编译以下内容:
rcu_read_lock
current->rcu_read_lock_nesting++;
barrier();
是一个编译指令,可以编译为空。
因此,根据英特尔的X86-64内存订购白皮书:
旧商店可能会将货物重新排序到其他位置
为什么实际上可以执行?
请考虑以下情况:
barrier
是什么阻止rcu_read_lock();
read_non_atomic_stuff();
rcu_read_unlock();
越过read_non_atomic_stuff
越过rcu_read_lock
,从而使其与在另一个线程中运行的回收代码同时运行?
答案 0 :(得分: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是将synchronize_rcu()
实现为for_each_possible_cpu(cpu) run_on(cpu);
的“经典RCU”,以使回收器在可能已执行RCU操作的每个核心(即每个核心)上执行。完成此操作后,我们知道在切换过程中某个地方一定已经发生了内存不足的情况。
所以,是的,RCU不遵循经典方法,在这种情况下,您需要一个完整的内存屏障(包括StoreLoad)来使核心等待直到第一个存储区可见之后才能进行任何读取。 RCU避免了读取路径中整个内存屏障的开销。除了避免争用外,这是它的主要吸引力之一。