内存栅栏是否涉及内核
Does the memory fence involve the kernel
问了才明白,原子指令,比如test-and-set
,是不会涉及到内核的。只有当一个进程需要进入休眠(等待获取锁)或唤醒(因为它无法获取锁但现在可以)时,内核才需要参与执行调度操作。
如果是这样,是否意味着内存栅栏,如c++11中的std::atomic_thread_fence
,不会也涉及到内核?
在这个问题和你所引用的问题中,你混合了:
"it involves the kernel" 是什么意思?我猜你的意思是“(p)线程同步”:线程进入睡眠状态,一旦另一个 process/thread.
满足给定条件,线程就会唤醒
但是,cmpxchg 和内存栅栏等测试和设置原语是由微处理器汇编器提供的功能。内核同步原语最终基于它们提供系统和进程同步,使用内核中的共享状态 space 隐藏在内核调用之后。
您可以查看 futex source 以获取证据。
但是不,内存栅栏不涉及内核:它们被翻译成 simple assembler operations。与 cmpxchg.
相同
std::atomic 不涉及内核1
在几乎所有普通 CPU 上(我们在现实生活中编程的那种),内存屏障指令是非特权的,直接由编译器使用。编译器知道如何使用的方式相同发出像 x86 lock add [rdi], eax
for fetch_add
这样的指令(或者 lock xadd
如果你使用 return 值)。或者 在其他 ISA 上,它们使用 before/after 加载、存储和 RMW 来提供所需的顺序的字面上相同的屏障指令。 https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/
在某些任意假设的硬件 and/or 编译器上,当然一切皆有可能,即使这会对性能造成灾难性的影响。
在 asm 中,屏障只会让 this 核心等待,直到其他核心可见之前的一些(程序顺序)操作。这是一个纯粹的本地操作。 (至少,这就是真实 CPU 的设计方式,因此顺序一致性是可以恢复的,只需要本地屏障来控制加载 and/or 存储操作的本地排序。所有内核共享一个连贯的缓存视图,通过像 MESI 这样的协议。存在非一致的共享内存系统,但实现不会 运行 C++ std::thread 跨越它们,而且它们通常不会 运行 单系统映像内核.)
脚注 1:(即使是非无锁原子通常也使用轻量级锁定)。
此外,ARMv7 之前的 ARM 显然没有正确的内存屏障指令。在 ARMv6 上,GCC 使用 mcr p15, 0, r0, c7, c10, 5
作为屏障。
在此之前(g++ -march=armv5
和更早版本),GCC 不知道该做什么并调用 __sync_synchronize
(一个 libatomic GCC 辅助函数),希望以某种方式实现无论代码实际上 运行ning 在什么机器上。 可能 涉及假设的 ARMv5 多核系统上的系统调用,但二进制文件更有可能 运行ning 在 ARMv7 或 v8 系统上,其中库函数可以 运行一个dmb ish
。或者,如果它是一个单核系统,那么它可能是一个空操作,我认为。 (C++ 内存排序关心其他 C++ 线程,而不关心可能的硬件设备/DMA 所看到的内存顺序。通常实现假设多核系统,但这个库函数可能是可以使用单核实现的情况.)
例如,在 x86 上,std::atomic_thread_fence(std::memory_order_seq_cst)
编译为 mfence
。 std::atomic_thread_fence(std::memory_order_release)
等较弱的障碍只需要阻止编译时重新排序; x86 的 运行time 硬件内存模型已经是 acq/rel(seq-cst + 存储缓冲区)。所以没有任何与障碍相对应的汇编指令。 (C++ 库的一个可能实现是 GNU C asm("" ::: "memory");
,但是 GCC/clang 确实有内置屏障。)
std::atomic_signal_fence
只需要阻止编译时重新排序,即使在弱排序的 ISA 上也是如此,因为所有现实世界的 ISA 都保证在单个线程内执行将它自己的 操作视为按程序顺序发生。 (硬件通过让负载监听当前内核的存储缓冲区来实现这一点)。如果异步信号(或内核代码中断)在任何指令之后到达。
你可以自己看看code-genon the Godbolt compiler explorer:
#include <atomic>
void barrier_sc(void) {
std::atomic_thread_fence(std::memory_order_seq_cst);
}
x86: mfence
.
力量:sync
.
AArch64:dmb ish
("inner shareable" 一致性域上的完整障碍)。
带有 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:无
POWER:lwsync
(轻量级同步)。
AArch64:仍然dmb ish
手臂:仍然dmb ish
RISC-V:仍然fence iorw,iorw
void barrier_acq(void) {
std::atomic_thread_fence(std::memory_order_acquire);
}
x86:无
POWER:lwsync
(轻量级同步)。
AArch64:dmb ishld
(加载障碍,不必耗尽存储缓冲区)
ARM:仍然 dmb ish
,即使使用 -mcpu=cortex-a53
(ARMv8):/
RISC-V:仍然fence iorw,iorw
问了test-and-set
,是不会涉及到内核的。只有当一个进程需要进入休眠(等待获取锁)或唤醒(因为它无法获取锁但现在可以)时,内核才需要参与执行调度操作。
如果是这样,是否意味着内存栅栏,如c++11中的std::atomic_thread_fence
,不会也涉及到内核?
在这个问题和你所引用的问题中,你混合了:
"it involves the kernel" 是什么意思?我猜你的意思是“(p)线程同步”:线程进入睡眠状态,一旦另一个 process/thread.
满足给定条件,线程就会唤醒但是,cmpxchg 和内存栅栏等测试和设置原语是由微处理器汇编器提供的功能。内核同步原语最终基于它们提供系统和进程同步,使用内核中的共享状态 space 隐藏在内核调用之后。
您可以查看 futex source 以获取证据。
但是不,内存栅栏不涉及内核:它们被翻译成 simple assembler operations。与 cmpxchg.
相同std::atomic 不涉及内核1
在几乎所有普通 CPU 上(我们在现实生活中编程的那种),内存屏障指令是非特权的,直接由编译器使用。编译器知道如何使用的方式相同发出像 x86 lock add [rdi], eax
for fetch_add
这样的指令(或者 lock xadd
如果你使用 return 值)。或者 在其他 ISA 上,它们使用 before/after 加载、存储和 RMW 来提供所需的顺序的字面上相同的屏障指令。 https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/
在某些任意假设的硬件 and/or 编译器上,当然一切皆有可能,即使这会对性能造成灾难性的影响。
在 asm 中,屏障只会让 this 核心等待,直到其他核心可见之前的一些(程序顺序)操作。这是一个纯粹的本地操作。 (至少,这就是真实 CPU 的设计方式,因此顺序一致性是可以恢复的,只需要本地屏障来控制加载 and/or 存储操作的本地排序。所有内核共享一个连贯的缓存视图,通过像 MESI 这样的协议。存在非一致的共享内存系统,但实现不会 运行 C++ std::thread 跨越它们,而且它们通常不会 运行 单系统映像内核.)
脚注 1:(即使是非无锁原子通常也使用轻量级锁定)。
此外,ARMv7 之前的 ARM 显然没有正确的内存屏障指令。在 ARMv6 上,GCC 使用 mcr p15, 0, r0, c7, c10, 5
作为屏障。
在此之前(g++ -march=armv5
和更早版本),GCC 不知道该做什么并调用 __sync_synchronize
(一个 libatomic GCC 辅助函数),希望以某种方式实现无论代码实际上 运行ning 在什么机器上。 可能 涉及假设的 ARMv5 多核系统上的系统调用,但二进制文件更有可能 运行ning 在 ARMv7 或 v8 系统上,其中库函数可以 运行一个dmb ish
。或者,如果它是一个单核系统,那么它可能是一个空操作,我认为。 (C++ 内存排序关心其他 C++ 线程,而不关心可能的硬件设备/DMA 所看到的内存顺序。通常实现假设多核系统,但这个库函数可能是可以使用单核实现的情况.)
例如,在 x86 上,std::atomic_thread_fence(std::memory_order_seq_cst)
编译为 mfence
。 std::atomic_thread_fence(std::memory_order_release)
等较弱的障碍只需要阻止编译时重新排序; x86 的 运行time 硬件内存模型已经是 acq/rel(seq-cst + 存储缓冲区)。所以没有任何与障碍相对应的汇编指令。 (C++ 库的一个可能实现是 GNU C asm("" ::: "memory");
,但是 GCC/clang 确实有内置屏障。)
std::atomic_signal_fence
只需要阻止编译时重新排序,即使在弱排序的 ISA 上也是如此,因为所有现实世界的 ISA 都保证在单个线程内执行将它自己的 操作视为按程序顺序发生。 (硬件通过让负载监听当前内核的存储缓冲区来实现这一点)。如果异步信号(或内核代码中断)在任何指令之后到达。
你可以自己看看code-genon the Godbolt compiler explorer:
#include <atomic>
void barrier_sc(void) {
std::atomic_thread_fence(std::memory_order_seq_cst);
}
x86: mfence
.
力量:sync
.
AArch64:dmb ish
("inner shareable" 一致性域上的完整障碍)。
带有 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:无
POWER:lwsync
(轻量级同步)。
AArch64:仍然dmb ish
手臂:仍然dmb ish
RISC-V:仍然fence iorw,iorw
void barrier_acq(void) {
std::atomic_thread_fence(std::memory_order_acquire);
}
x86:无
POWER:lwsync
(轻量级同步)。
AArch64:dmb ishld
(加载障碍,不必耗尽存储缓冲区)
ARM:仍然 dmb ish
,即使使用 -mcpu=cortex-a53
(ARMv8):/
RISC-V:仍然fence iorw,iorw