从shm_open()+ mmap()更改共享内存的可见性

时间:2018-02-15 09:34:58

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

假设我在CentOS 7 x86_64 + GCC 7上。

我想在共享内存中创建一个ringbuffer。

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

如果制作人写了类似的内容:

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

随机时间到共享内存,而Consumer正在不断轮询共享内存以进行读取。我是否会遇到某种同步问题,即成员长度可见,但成员数据仍处于编写过程中?如果是,那么避免这个问题的最有效技术是什么?

我看到这篇文章: Shared-memory IPC synchronization (lock-free)

但我希望能够更深入,更低级地了解在两个流程之间有效同步所需的内容。

提前致谢!

2 个答案:

答案 0 :(得分:3)

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

此外,还有POSIX中的锁定原语,但<atomic>标题更新,你可能想要它。

标准所说的

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

  

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

对于可锁定原子,标准在[thread.rec.lockable.general]中说,重点是:

  

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

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

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

答案 1 :(得分:0)

是的,您最终会遇到数据竞赛,不仅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在消费者呼叫sem_wait时写入shm,则消费者将阻止,直到Producer将呼叫sem_post,您无法保证制作人不会再去另一个循环,在消费者醒来之前连续写两次。你必须建立一个不确定生产者和机制的机制。消费者可以另外工作。