在Linux/x86上mmapping/dev/shmWrite-Back(WB)或者Non-CacheableWrite-Combining(WC)返回的内存是多少?

Is the memory returned from mmapping /dev/shm Write-Back (WB) or Non-Cacheable Write-Combining (WC) on Linux/x86?

我有两个 C++ 进程通过 memory-mapped Single-Producer Single-Consumer (SPSC) double buffer 进行通信。这些进程只会在 Linux/Intel x86-64 上 运行。语义是生产者填充前台缓冲区,然后交换指针并更新计数器,让消费者知道它可以 memcpy() 后台缓冲区。所有共享状态都存储在映射区域开始处的 header 块中。

int _fd;
volatile char *_mappedBuffer;

...

_fd = shm_open("/dev/shm/ipc_buffer", O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
...
_mappedBuffer = static_cast<char *>(mmap(nullptr, _shmFileSizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE | MAP_POPULATE, _fd, 0));

生产者需要一个 StoreStore 屏障来确保交换在计数器递增之前可见,这在 x86 上应该是隐式的,with Write-Back (WB) memory:

void produce() {
    ...

    // swap pointers
    char *tmp = _frontBuffer;
    _frontBuffer= _backBuffer;
    _backBuffer= tmp;

    ...

    // SFENCE needed here? Yes if uncacheable WC, NO if WB due to x86 ordering guarantees?
    asm volatile ("sfence" ::: "memory");

    _flipCounter++;
}

消费者需要一个 LoadLoad 屏障 if (WC) 以确保它在新的后台缓冲区指针之前加载翻转计数器。如果内存是 (WB),那么我们知道 CPU 不能 re-order 负载:

bool consume(uint64_t &localFlipVer, char *dst) {
    if (localFlipVer < _flipCounter) {
        // LFENCE needed here? Yes if uncacheable WC, NO if WB due to x86 ordering guarantees?
        asm volatile ("lfence" ::: "memory");

        std::memcpy(dst, _backBuffer, _bufferSize);
        localFlipVer++;
        return true;
    }

    return false;
}

我的问题和我的假设

mmmap/dev/shmWrite-Back还是Non-cacheableWrite-Combining返回的memory-mapped区域?如果是后者,根据

,存储和加载是弱排序的并且不遵循传统的 x86 排序保证(没有 StoreStore 或 LoadLoad re-orderings)

https://hadibrais.wordpress.com/2019/02/26/the-significance-of-the-x86-sfence-instruction/

https://preshing.com/20120913/acquire-and-release-semantics/#IDComment721195741

https://software.intel.com/en-us/forums/software-tuning-performance-optimization-platform-monitoring/topic/596002

因此,我必须使用 SFENCELFENCE,而通常情况下(使用 WB),我可以只用一个编译器障碍 asm volatile ("" ::: "memory");

/dev/shm/ 只是一个 tmpfs 挂载点,就像 /tmp.

记忆你mmap在文件中有正常的WB可缓存,就像MAP_ANONYMOUS。它遵循正常的 x86 内存排序规则(程序顺序 + 带存储转发的存储缓冲区),因此您不需要 SFENCE 或 LFENCE,只阻止 acq_rel 排序的编译时重新排序。或者对于seq_cst,MFENCE或者锁定操作,比如使用xchg来存储。

对于 lock_free 类型,您可以在指向 SHM 的指针上使用 C11 <stdatomic.h> 函数。 (通常是指针宽度的任何 2 的幂大小。)

非无锁对象在执行操作的进程的地址-space中使用锁的散列table,因此不同的进程不会尊重彼此的锁。 16 字节对象仍然可以使用 lock cmpxchg16b,它是无地址的并且可以跨进程工作,即使 GCC7 和更高版本将其报告为非无锁 for reasons 即使您使用 -mcx16 进行编译.


我认为在主流 Linux 内核中没有办法为用户 space 分配 WB 以外的任何类型的内存。 (X 服务器或直接渲染客户端映射视频 RAM 除外;我的意思是无法映射具有不同 PAT 内存类型的普通 DRAM 页面。)另请参阅

对于不尝试将存储批量存储到一个宽 SIMD 存储中的普通代码,除 WB 之外的任何类型都将是潜在的性能灾难。例如如果您在 SHM 中有一个受共享互斥锁保护的数据结构,如果关键部分内的正常访问不可缓存,那将很糟糕。特别是在同一个线程重复获取同一个锁和reading/writing同一个数据的无竞争情况下。

所以它总是 WB 是有充分理由的。