简体   繁体   English

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

[英]Visibility of change to shared memory from shm_open() + mmap()

Let's say I am on CentOS 7 x86_64 + GCC 7. 假设我在CentOS 7 x86_64 + GCC 7上。

I would like to create a ringbuffer in shared memory. 我想在共享内存中创建一个ringbuffer。

If I have two processes Producer and Consumer, and both share a named shared memory, which is created/accessed through shm_open() + mmap(). 如果我有两个进程Producer和Consumer,并且两者共享一个命名的共享内存,它通过shm_open()+ mmap()创建/访问。

If Producer writes something like: 如果制作人写了类似的东西:

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

to the shared memory at a random time, and the Consumer is constantly polling the shared memory to read. 随机时间到共享内存,消费者不断轮询共享内存进行读取。 Will I have some sort of synchronization issue that the member length is seen but the member data is still in the progress of writing? 我是否会遇到某种同步问题,即成员长度可见,但成员数据仍处于编写过程中? If yes, what's the most efficient technique to avoid the issue? 如果是,那么避免这个问题的最有效技术是什么?

I see this post: Shared-memory IPC synchronization (lock-free) 我看到这篇文章: 共享内存IPC同步(无锁)

But I would like to get a deeper, more low level of understanding what's required to synchronize between two processes efficiently. 但我希望能够更深入,更低级地了解在两个流程之间有效同步所需的内容。

Thanks in advance! 提前致谢!

To avoid this, you would want to make the structure std::atomic and access it with acquire-release memory ordering. 为避免这种情况,您可能希望使结构std::atomic并使用acquire-release内存排序访问它。 On most modern processors, the instructions this inserts are memory fences, which guarantee that the writer wait for all loads to complete before it begins writing, and that the reader wait for all stores to complete before it begins reading. 在大多数现代处理器上,插入的指令是内存屏障,它保证编写器在开始写入之前等待所有负载完成,并且读取器在开始读取之前等待所有存储完成。

There are, in addition, locking primitives in POSIX, but the <atomic> header is newer and what you probably want. 此外,还有POSIX中的锁定原语,但<atomic>标题更新,你可能想要的。

What the Standard Says 标准说的是什么

From [atomics.lockfree], emphasis added: 来自[atomics.lockfree],重点补充说:

Operations that are lock-free should also be address-free. 无锁的操作也应该是无地址的。 That is, atomic operations on the same memory location via two different addresses will communicate atomically. 也就是说,通过两个不同地址在同一存储器位置上的原子操作将以原子方式进行通信。 The implementation should not depend on any per-process state. 实现不应该依赖于任何每个进程的状态。 This restriction enables communication by memory that is mapped into a process more than once and by memory that is shared between two processes. 此限制允许通过多次映射到进程的内存以及在两个进程之间共享的内存进行通信。

For lockable atomics, the standard says in [thread.rec.lockable.general], emphasis added: 对于可锁定的原子,标准在[thread.rec.lockable.general]中说,重点是:

An execution agent is an entity such as a thread that may perform work in parallel with other execution agents. 执行代理是诸如可以与其他执行代理并行执行工作的线程之类的实体。 [...] Implementations or users may introduce other kinds of agents such as processes [....] [...]实施或用户可能会引入其他类型的代理, 例如流程 [....]

You will sometimes see the claim that the standard supposedly makes no mention of using the <atomic> primitives with memory shared between processes, only threads. 您有时会看到声称该标准没有提及使用<atomic>原语与进程之间共享的内存,只有线程。 This is incorrect. 这是不正确的。

However, passing pointers to the other process through shared memory will not work, as the shared memory may be mapped to different parts of the address space, and of course a pointer to any object not in shared memory is right out. 但是,通过共享内存将指针传递给另一个进程将无法工作,因为共享内存可能会映射到地址空间的不同部分,当然,指向不在共享内存中的任何对象的指针也是正确的。 Indices and offsets of objects within shared memory will. 共享内存中对象的索引和偏移量。 (Or, if you really need pointers, Boost provides IPC-safe wrappers.) (或者,如果你真的需要指针,Boost提供IPC安全包装器。)

Yes, you will ultimately run into data races, not only length being written and read before data is written, but also parts of those members will be written out of sync of your process reading it. 是的,您最终会遇到数据争用,不仅是在写入data之前写入和读取的length ,而且这些成员的一部分将与您的进程读取不同步。

Although lock-free is the new trend, I'd suggest to go for a simpler tool as your first IPC sync job: the semaphore. 虽然无锁是一种新趋势,但我建议你选择一个更简单的工具作为你的第一个IPC同步工作:信号量。 On linux, the following man pages will be useful: 在linux上,以下手册页将非常有用:

The idea is to have both processes signal the other one it is currently reading or writing the shared memory segment. 这个想法是让两个进程发信号通知它正在读取或编写共享内存段的另一个进程。 With a semaphore, you can write inter-process mutexes: 使用信号量,您可以编写进程间互斥:

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)

If for instance Producer is writing into shm while Consumer calls sem_wait , Consumer will block until after Producer will call sem_post , but , you have no guarantee Producer won't go for another loop, writing two times in a row before Consumer will be woke up. 如果例如Producer写入shm而Consumer调用sem_wait ,则Consumer将阻塞,直到Producer将调用sem_post但是 ,你无法保证 Producer不会再去另一个循环,连续两次写入消费者将被唤醒。 You have to build a mechanism unsure Producer & Consumer do work alternatively. 你必须建立一个不确定生产者和消费者的机制。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM