gpt4 book ai didi

c++ - 如何在 C++14 中将多字节值写入共享内存?

转载 作者:行者123 更新时间:2023-12-03 06:51:29 25 4
gpt4 key购买 nike

假设我有两个使用 shm_open 共享内存块的进程。和 mmap并且存在一个共享同步原语 - 比如说信号量 - 确保对内存的独占访问。 IE。没有比赛条件。
我的理解是mmap返回的指针仍必须将其标记为 volatile 以防止缓存读取。
现在,如何写例如一个 std::uint64_t进入内存中的任何对齐位置?
当然,我会简单地使用 std::memcpy但它不适用于指向 volatile 内存的指针。
第一次尝试

// Pointer to the shared memory, assume it is aligned correctly.
volatile unsigned char* ptr;

// Value to store, initialize "randomly" to prevent compiler
// optimization, for testing purposes.
std::uint64_t value = *reinterpret_cast<volatile std::uint64_t*>(nullptr);

// Store byte-by-byte
unsigned char* src = reinterpret_cast<unsigned char*>(&value);
for(std::size_t i=0;i<sizeof(value);++i)
ptr[i]=src[i];
Godbolt .
我坚信这个解决方案是正确的,但即使是 -O3 ,有 8 个 1 字节传输。这确实不是最优的。
第二次尝试
既然我知道在我锁定内存时没有人会更改内存,那么 volatile 可能毕竟是不必要的?
// Pointer to the shared memory, assume it is aligned correctly.
volatile unsigned char* ptr;

// Value to store, initialize "randomly" to prevent compiler
// optimization for testing purposes.
std::uint64_t value = *reinterpret_cast<volatile std::uint64_t*>(0xAA);
unsigned char* src = reinterpret_cast<unsigned char*>(&value);

//Obscure enough?
auto* real_ptr = reinterpret_cast<unsigned char*>(reinterpret_cast<std::uintptr_t>(ptr));

std::memcpy(real_ptr,src,sizeof(value));
Godbolt .
但这似乎不起作用,编译器看穿了类型转换并且什么也不做。 Clang 生成 ud2指令,不知道为什么,我的代码中有 UB 吗?除了 value初始化。
第三次尝试
这个来自 this answer .但我认为它确实违反了严格的别名规则,不是吗?
// Pointer to the shared memory, assume it is aligned correctly.
volatile unsigned char* ptr;

// Value to store, initialize "randomly" to prevent compiler
// optimization for testing purposes.
std::uint64_t value = *reinterpret_cast<volatile std::uint64_t*>(0xAA);
unsigned char* src = reinterpret_cast<unsigned char*>(&value);

volatile std::uint64_t* dest = reinterpret_cast<volatile std::uint64_t*>(ptr);
*dest=value;
Godbolt .
Gcc 实际上做了我想要的——一个简单的复制 64 位值的指令。但是如果是UB就没用了。
我可以解决它的一种方法是真正创建 std::uint64_t那个地方的对象。但是,显然placement new 不适用于 volatile指针。
问题
  • 那么,有没有比逐字节复制更好(安全)的方法?
  • 我还想复制更大的原始字节 block 。这可以比单个字节做得更好吗?
  • 有没有可能强制memcpy做正确的事?
  • 我是否不必要地担心性能并且应该只使用循环?
  • 任何示例(主要是 C)都不要使用 volatile完全,我也应该这样做吗?是 mmap ed 指针已经被区别对待了吗?如何?

  • 感谢您的任何建议。
    编辑:
    两个进程在同一个系统上运行。另外请假设这些值可以逐字节复制,而不是谈论存储指向某处的指针的复杂虚拟类。所有整数和没有浮点数就可以了。

    最佳答案

    My understanding is that the pointer returned from mmap must still be marked as volatile to prevent cached reads.


    你的理解是错误的。不要使用 volatile用于控制内存可见性 - 这不是它的用途。它要么过于昂贵,要么不够严格,或两者兼而有之。
    例如,考虑 GCC documentation on volatile ,其中说:

    Accesses to non-volatile objects are not ordered with respect to volatile accesses. You cannot use a volatile object as a memory barrier to order a sequence of writes to non-volatile memory


    如果您只是想避免撕裂、缓存和重新排序 - 使用 <atomic> 反而。例如,如果您有一个现有的共享 uint64_t (并且对齐正确),只需通过 std::atomic_ref<uint64_t> 访问它.你可以直接使用acquire、release或CAS。
    如果您需要正常同步,那么您现有的信号量就可以了。如下所示,它已经提供了任何必要的栅栏,并防止在等待/发布调用中重新排序。它不会阻止它们之间的重新排序或其他优化,但这通常没问题。

    至于

    Any examples(mostly C) do not use volatile at all, should I do that too? Is mmaped pointer treated differently already? How?


    答案是无论使用什么同步都需要应用适当的栅栏。
    POSIX lists these functions作为“同步内存”,这意味着它们必须发出任何所需的内存栅栏,并防止不适当的编译器重新排序。
    因此,例如,您的实现必须避免跨 pthread_mutex_*lock() 移动内存访问。或 sem_wait()/ sem_post()调用以符合 POSIX,即使在其他情况下它是合法的 C 或 C++。
    当您使用 C++ 的内置线程或原子支持时,正确的语义是语言标准的一部分,而不是平台扩展(但共享内存不是)。

    关于c++ - 如何在 C++14 中将多字节值写入共享内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64917288/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com