【问题标题】:Visibility of change to shared memory from shm_open() + mmap()从 shm_open() + mmap() 更改共享内存的可见性
【发布时间】:2018-07-26 00:20:46
【问题描述】:

假设我使用的是 CentOS 7 x86_64 + GCC 7。

我想在共享内存中创建一个环形缓冲区。

如果我有两个进程 Producer 和 Consumer,并且都共享一个命名的共享内存,它是通过 shm_open() + mmap() 创建/访问的。

如果 Producer 写了这样的内容:

struct Data {
uint64_t length;
char data[100];
}

随机到共享内存,并且Consumer不断轮询共享内存来读取。我是否会出现某种同步问题,即看到成员长度但成员数据仍在写入过程中?如果是,避免该问题的最有效技术是什么?

我看到这个帖子: Shared-memory IPC synchronization (lock-free)

但我想更深入、更深入地了解在两个进程之间有效同步所需的条件。

提前致谢!

【问题讨论】:

    标签: c++ c linux posix shared-memory


    【解决方案1】:

    是的,您最终会遇到数据竞争,不仅 length 在写入和读取 data 之前被写入和读取,而且这些成员的一部分将被写入与您读取它的进程不同步。

    尽管无锁是新趋势,但我建议您使用更简单的工具作为您的第一个 IPC 同步工作:信号量。在 linux 上,以下手册页将很有用:

    这个想法是让两个进程向另一个进程发出信号,它当前正在读取或写入共享内存段。使用信号量,您可以编写进程间互斥锁:

    Producer:
    while true:
        (opt) create resource
        lock semaphore (sem_wait)
        copy resource to shm
        unlock semaphore (sem_post)
    
    Consumer:
    while true:
        lock semaphore (sem_wait)
        copy resource to local memory
            or crunch resource
        unlock semaphore (sem_post)
    

    例如,如果 Producer 在 Consumer 调用 sem_wait 时写入 shm,Consumer 将阻塞直到 Producer 调用 sem_post但是,你有不能保证 Producer 不会再循环,在 Consumer 被唤醒之前连续写两次。您必须建立一种机制,不确定生产者和消费者是否可以交替工作。

    【讨论】:

    • 感谢您的快速回复。链接很有用。现在阅读。我似乎涉及某种记忆障碍?由于它需要以某种方式使缓存无效,因此消费者必须强制缓存未命中或类似情况,因此将从共享内存中获取长度和数据。这就是我试图达到的“更深层次、更低层次的理解”。谢谢!
    【解决方案2】:

    为避免这种情况,您需要创建结构std::atomic 并使用获取-释放内存顺序访问它。在大多数现代处理器上,此插入的指令是内存栅栏,它保证写入器在开始写入之前等待所有加载完成,并且读取器在开始读取之前等待所有存储完成。

    此外,POSIX 中还有锁定原语,但 <atomic> 标头较新,可能是您想要的。

    标准是怎么说的

    来自 [atomics.lockfree],重点补充:

    无锁的操作也应该是无地址的。也就是说,通过两个不同地址对同一内存位置的原子操作将以原子方式进行通信。实现不应依赖于任何每个进程的状态。此限制允许通过多次映射到进程的内存和在两个进程之间共享的内存进行通信。

    对于可锁定的原子,标准在 [thread.rec.lockable.general] 中说,强调:

    执行代理是一个实体,例如可以与其他执行代理并行执行工作的线程。 [...] 实现或用户可能会引入其他类型的代理例如进程 [....]

    您有时会看到声称该标准没有提及使用<atomic> 原语在进程之间共享内存,只有线程。这是不正确的。

    但是,通过共享内存传递指向另一个进程的指针是行不通的,因为共享内存可能被映射到地址空间的不同部分,当然指向不在共享内存中的任何对象的指针都是正确的。共享内存中对象的索引和偏移量将。 (或者,如果你真的需要指针,Boost 提供了 IPC 安全的包装器。)

    【讨论】:

    • 对,我认为 std::atomic 对于在线程之间共享数据非常有用。但是这一次,我想在进程之间共享。
    • 添加了标准中关于使用<atomic> 在进程之间共享内存的内容。
    • 感谢您引用标准。我想我可以使用 atomic 作为成员长度。但是会员数据呢?谢谢!
    • @Hei 请不要发表评论说“谢谢”、投票和/或接受答案。
    • @YSC 一旦我得到满意的答案,我会的。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2019-04-05
    • 2015-08-30
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 2019-11-28
    • 1970-01-01
    • 2014-02-14
    相关资源
    最近更新 更多