14

我在多个进程之间有一个共享内存,它以某种方式对内存进行交互。前任:

DataBlock {
int counter;
double value1;
double ...    }

我想要的是让计数器自动更新/递增。并在该地址上发生内存释放。例如,如果我没有使用共享内存,它将类似于

std::atomic<int> counter;
atomic_store(counter, newvalue, std::memory_order_release); // perform release     operation on the affected memory location making the write visible to other threads

我如何为随机内存位置实现这一点(解释为 DataBlock 计数器 >above)。我可以保证地址按照架构的要求对齐(x86 linux)

  1. 使更新原子化 - 如何?(即 atomicupdate(addr, newvalue))
  2. 多核的内存同步 - (即 memorysync(addr)) - 我能看到的唯一方法是使用 std::atomic_thread_fence(std::memory_order_release) - 但这将“建立所有原子和宽松原子存储的内存同步排序” - 就是这样对我来说有点矫枉过正——我只想同步计数器位置。欣赏任何想法。
4

3 回答 3

14

我不能在这里权威地回答,但我可以提供可能有帮助的相关信息。

  1. 互斥锁可以在共享内存中创建和/或创建为跨进程。Pthread 有一个特殊的创建标志,我不记得它是否使用共享内存,或者你共享一个句柄。linux“futex”可以直接使用共享内存(注意用户地址可能不同,但底层真实地址应该相同)

  2. 硬件原子在内存而不是进程变量上工作。也就是说,您的芯片不会关心哪些程序正在修改变量,因此最低级别的原子自然会是跨进程的。这同样适用于栅栏。

  3. C++11 未能指定跨进程原子。但是,如果它们是无锁的(检查标志),则很难看出编译器如何实现它们以使跨进程无法工作。但是您会非常信任您的工具链和最终平台。

  4. CPU 依赖性保证还跟踪实际内存地址,因此只要您的程序在线程形式中是正确的,它在其多进程形式中也应该是正确的(关于可见性)。

  5. Kerrek 是正确的,抽象机并没有真正提到多个进程。但是,它的同步细节以这样一种方式编写,即它们同样适用于进程间,就像它们适用于多线程一样。这与 #3 有关:编译器很难搞砸。

简短的回答,没有符合标准的方法来做到这一点。但是,根据标准定义多线程的方式,您可以为高质量的编译器做出很多假设。

最大的问题是是否可以简单地在共享内存中分配原子(放置新)并工作。显然,这只有在它是真正的硬件原子时才有效。然而,我的猜测是,使用高质量的编译器/库,C++ 原子应该可以在共享内存中找到。

玩得开心验证行为。:)

于 2012-01-06T17:30:22.157 回答
6

由于您在 Linux 上,因此您可以根据 atomic built-ins 上的gccgcc文档在__sync_fetch_and_add()地址上使用 atomic 内置,这也将实现完整的内存围栏,而不是释放操作,但因为你实际上想要一个读-修改-写操作而不是简单的加载(即,增加一个计数器不仅仅是一个加载,但你必须读取,然后修改,最后写回值),全内存围栏将是执行此操作的正确内存排序的更好选择。counter

于 2012-01-06T19:01:59.017 回答
1

我正在查看标准草案 N4820 [atomics.lockfree],它说:

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

因此,如果您的目标是无地址,则先决条件是无锁的,这可以通过 std::atomic 进行检查。

但是,我不确定如何atomic创建对象。将对象放在共享内存中是否足够好?我还没有找到任何关于这种用法的规范,而我确实在 github 上看到了这样的代码用法。

(编者注:您将指针转换为共享内存,例如
auto p = static_cast<atomic<int>*>(ptr); 与访问共享内存的任何其他类型相同。)

于 2019-06-27T08:45:19.400 回答