c - 系统崩溃时 clflush 或 clflushopt 是原子的吗?

标签 c x86-64 intel atomic persistent-memory

通常,缓存行是 64B,但非 volatile 内存的原子性是 8B。
例如:

x[1]=100;
x[2]=100;
clflush(x);
x缓存行对齐,初始设置为 0 .
系统崩溃 clflush();可以吗x[1]=0 , x[2]=100重启后?

最佳答案

在以下假设下:

  • 我假设您显示的代码表示一系列 x86 汇编指令,而不是尚未编译的实际 C 代码。
  • 我还假设代码是在 Cascade Lake 处理器上执行的,而不是在新一代 Intel 处理器上执行(我认为带有 Barlow Pass 的 CPL 或 ICX 支持 eADR,这意味着持久性不需要显式刷新,因为缓存位于持久域)。这个答案也适用于现有的 AMD+NVDIMM 平台。

  • 存储的全局可观察性顺序可能与 Intel x86 处理器上的持久顺序不同。这称为松弛持久性。唯一保证顺序相同的情况是将 WB 类型的存储序列放入同一缓存行(但到达 GO 的存储并不一定意味着它变得持久)。这是因为 CLFLUSH是原子的,WB 存储不能在全局可观察性中重新排序。见:On x86-64, is the “movnti” or "movntdq" instruction atomic when system crash? .
    如果两个存储跨越缓存线边界或者如果存储的有效内存类型是 WC:
    x86-TSO 内存模型不允许重新排序存储,因此另一个代理不可能观察到 x[2] == 100x[1] != 100在正常操作期间(即在没有崩溃的 volatile 状态下)。但是,如果系统崩溃并重新启动,则持久状态可能为 x[2] == 100x[1] != 100 .即使退休后系统崩溃,这也是可能的clflush因为clflush的退休并不一定意味着刷新的缓存行已到达持久域。
    如果你想消除它,你可以移动 clflush如下:
    x[1]=100;
    clflush(x);
    x[2]=100;
    
    clflush英特尔处理器上的所有写入都是按顺序排列的,这意味着该行保证在任何后续存储变为全局可见之前到达持久域。见:Persistent Memory Programming Primary (PDF)和英特尔 SDM V2。第二家商店可以在同一行或任何其他行。
    如果你想要 x[1]=100在之前变得持久 x[2]=100成为全局可观察的,添加 sfence之后 clflush在英特尔 CSX 或 mfence 上在 AMD 处理器上(clflush 仅由 mfence 在 AMD 处理器上订购)。 clflush本身足以控制持久顺序。
    或者,使用序列 clflushopt+sfence (或 clwb+sfence )如下:
    x[1]=100;
    clflushopt(x);
    sfence;
    x[2]=100;
    
    在这种情况下,如果发生崩溃并且如果 x[2] == 100在持久状态下,则保证 x[1] == 100 . clflushopt本身不会强加任何持久排序。

    关于c - 系统崩溃时 clflush 或 clflushopt 是原子的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65439089/

    相关文章:

    c - 什么时候使用 pthread_spin_lock 是正确的(例如 pthread mutex)?

    c - leetcode 38 count and say confuse me的c解法

    c++ - 跟踪反汇编代码中的调用堆栈

    c++ - 无法将 'tbb/atomic.h' 与 Intel 编译器一起使用

    opencl - 许多 OpenCL SDK。我应该选择哪一个?

    java - 在 Android 平台上从 java 代码读取/写入/proc/<pid>/mem

    c - 将数据文件作为链表中的节点读取的函数

    assembly - x86-64 规范地址?

    assembly - gdb 对 .bss 中的符号和 .data 中的符号的行为不同

    linux - 无法登录 Yocto qemux86-64