内存围栏是否涉及内核

Does the memory fence involve the kernel

本文关键字:内核 是否 内存      更新时间:2023-10-16

问了这个问题之后,我明白了原子指令,比如test-and-set,不会涉及内核。仅当进程需要进入睡眠状态(等待获取锁)或唤醒(因为它无法获取锁,但现在可以获取锁)时,才必须涉及内核来执行调度操作。

如果是这样,这是否意味着内存围栏(例如 c++11 中的std::atomic_thread_fence)也不会涉及内核?

std::atomic 不涉及内核1

在几乎所有普通的CPU(我们在现实生活中编程的那种)上,内存屏障指令都是无特权的,由编译器直接使用。就像编译器知道如何发出 x86lock add [rdi], eaxforfetch_add(如果使用返回值,则发出lock xadd指令)的方式相同。 或者在其他 ISA 上,它们在加载、存储和 RMW 之前/之后使用的相同屏障指令来提供所需的排序。https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/

在某些任意假设的硬件和/或编译器上,一切当然都是可能的,即使这对性能来说是灾难性的。

在 asm 中,屏障只是让这个核心等到其他核心可以看到以前的一些(程序顺序)操作。这是一个纯粹的本地操作。 (至少,这是实字 CPU 的设计方式,因此顺序一致性是可恢复的,只有本地屏障来控制加载和/或存储操作的本地顺序。 所有内核共享一个连贯的缓存视图,通过MESI等协议进行维护。 存在非一致的共享内存系统,但实现不会C++ std::thread 上运行,并且它们通常不会运行单系统映像内核。

脚注1:(即使是非无锁原子通常也使用轻量级锁定)。

此外,ARMv7之前的ARM显然没有适当的内存屏障指令。 在ARMv6上,GCC使用mcr p15, 0, r0, c7, c10, 5作为屏障。
在此之前(g++ -march=armv5及更早),GCC 不知道该怎么做并调用__sync_synchronize(一个 libatomic GCC 辅助函数),希望它以某种方式被嵌入代码实际运行的任何机器上。这可能涉及对假设的 ARMv5 多核系统的系统调用,但更有可能的是,二进制文件将在库函数可以运行dmb ish的 ARMv7 或 v8 系统上运行。 或者,如果它是一个单核系统,那么我认为它可能是一个无操作。 (C++内存排序关心其他C++线程,而不是可能的硬件设备/DMA看到的内存顺序。 通常,实现假定多核系统,但此库函数可能是可以使用仅单核实现的情况。


例如,在 x86 上,std::atomic_thread_fence(std::memory_order_seq_cst)编译为mfence. 像std::atomic_thread_fence(std::memory_order_release)这样的较弱屏障只需要阻止编译时重新排序;x86 的运行时硬件内存模型已经是 ACQ/REL(seq-cst + 存储缓冲区)。 因此,没有任何与屏障对应的asm指令。 (C++库的一个可能的实现是GNU Casm("" ::: "memory");,但GCC/clang确实有内置的屏障。

std::atomic_signal_fence只需要阻止编译时重新排序,即使在弱序 ISA 上也是如此,因为所有实际 ISA 都保证单个线程中的执行将其自己的操作视为按程序顺序发生。 (硬件通过让加载窥探当前内核的存储缓冲区来实现这一点)。 VLIW 和 IA-64 EPIC 或其他显式并行 ISA 机制(如 Mill 及其延迟可见性负载)仍然使编译器能够生成遵守任何涉及屏障的C++排序保证的代码,如果异步信号(或内核代码中断)在任何指令之后到达。


你可以在Godbolt编译器资源管理器上自己查看代码生成:

#include <atomic>
void barrier_sc(void) {
std::atomic_thread_fence(std::memory_order_seq_cst);
}

x86:mfence
力量:sync
AArch64:dmb ish("内部可共享"一致性域的完全屏障)。
gcc -mcpu=cortex-a15(或-march=armv7)的 ARM :dmb ish
RISC-V:fence iorw,iorw

void barrier_acq_rel(void) {
std::atomic_thread_fence(std::memory_order_acq_rel);
}

x86:无
电源:lwsync(轻量级同步)。
AArch64:仍然dmb ish
臂:仍然dmb ish
RISC-V:仍然fence iorw,iorw

void barrier_acq(void) {
std::atomic_thread_fence(std::memory_order_acquire);
}

x86:无
电源:lwsync(轻量级同步)。
AArch64:dmb ishld(负载屏障,不必耗尽存储缓冲区)
ARM:仍然dmb ish,即使有-mcpu=cortex-a53(ARMv8):/
RISC-V:仍然fence iorw,iorw

在这个问题和你混合的参考问题中:

  • 汇编程序范围内的同步基元,如 CMPXCHG 和栅栏
  • 进程/线程同步,如 futexes

"它涉及内核"是什么意思?我猜你的意思是"(p)线程同步":线程进入睡眠状态,一旦另一个进程/线程满足给定条件就会唤醒。

但是,像 cmpxchg 和内存围栏这样的测试和设置基元是由微处理器汇编程序提供的功能。内核同步原语最终基于它们来提供系统和进程同步,使用隐藏在内核调用后面的内核空间中的共享状态。

您可以查看 futex 源代码以获取它的证据。

但是不,内存围栏不涉及内核:它们被转换为简单的汇编程序操作。与 cmpxchg 相同。