33

我想为在 Linux 嵌入式系统上运行的服务(守护程序)使用的少数参数添加网络控制。不需要过程调用,每个参数都可以以非常自然的方式轮询。共享内存似乎是一种很好的方式,可以将网络代码排除在守护进程之外,并将共享访问限制为一组精心控制的变量。

由于我不希望部分写入导致从未写入的值的可见性,因此我正在考虑使用std::atomic<bool>and std::atomic<int>。但是,我担心它std::atomic<T>可能以仅适用于 C++11 线程而不适用于多个进程的方式实现(可能甚至不适用于 OS 线程)。具体来说,如果实现使用存储在共享内存块之外的任何数据结构,在多进程场景中,这将失败。

我确实看到了一些要求,这些要求std::atomic不包含嵌入式锁对象或指向其他数据的指针:

原子积分特化和特化atomic<bool>应有标准布局。它们每个都应该有一个普通的默认构造函数和一个普通的析构函数。它们都应支持聚合初始化语法。

原子类模板应该有指针部分特化。这些特化应具有标准布局、平凡的默认构造函数和平凡的析构函数。它们都应支持聚合初始化语法。

在我看来,微不足道的默认构造和破坏似乎排除了关联的每个对象数据,无论是存储在对象内部、通过指针成员变量还是通过外部映射。

但是,我没有看到任何排除使用单个全局互斥锁/关键部分(甚至是全局集合,只要集合元素不与单个原子对象相关联——类似于缓存关联方案的内容)的实现可用于减少错误冲突)。显然,在使用全局互斥锁的实现上,来自多个进程的访问会失败,因为用户将拥有独立的互斥锁,并且实际上并不彼此同步。

是否atomic<T>允许执行与进程间共享内存不兼容的事情,或者是否有其他规则使其安全?


我刚刚注意到微不足道的默认构造使对象处于未就绪状态,atomic_init因此需要调用。标准提到了锁的初始化。如果这些存储在对象内部(并且动态内存分配似乎是不可能的,因为析构函数仍然微不足道),那么它们将在进程之间共享。但我仍然担心全局互斥锁的可能性。

无论如何,保证对atomic_init共享区域中的每个变量进行一次调用似乎很困难……所以我想我必须避开 C++11 原子类型。

4

2 回答 2

23

我迟到了两个月,但我现在遇到了完全相同的问题,我想我已经找到了某种答案。简短的版本是它应该可以工作,但我不确定我是否会依赖它。

这是我发现的:

  • C++11 标准定义了一个新的内存模型,但它没有操作系统级“进程”的概念,所以任何与多处理相关的东西都是非标准的。

  • 但是,标准的第 29.4 节“无锁属性”(或至少我拥有的草案,N3337)以以下说明结尾:

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

    这听起来很有希望。:)

  • 该注释似乎来自N2427,它更加明确:

    为了通过共享内存促进进程间通信,我们的意图是无锁操作也是无地址的。也就是说,通过两个不同地址对同一内存位置的原子操作将以原子方式进行通信。实现不应依赖于任何每个进程的状态。虽然这样的定义超出了标准的范围,但明确说明我们的意图将使已经存在的程序类的可移植表达成为可能。

    所以看起来是的,所有无锁操作都应该在这个确切的场景中工作。

  • 现在,对 的操作std::atomic<type>是原子的,但对于特定的,它们可能是无锁的,也可能不是无锁的type,这取决于平台的能力。x我们可以通过调用来检查任何变量x.is_lock_free()

  • 那么为什么我写我不会依赖这个呢?我找不到任何关于 gcc、llvm 或其他任何明确说明的文档。

于 2013-11-12T18:41:27.567 回答
2

在 C++11 之前,该标准没有指定多个线程如何共享内存,因此我们编写了具有多个线程的程序,这些程序依赖于特定于实现的行为。该标准仍然没有指定共享内存的进程 - 或者如果您愿意,只部分共享内存的线程 - 交互。无论您最终做什么,您都将依赖于特定于实现的保证。

也就是说,我认为支持进程共享内存的实现将尝试使其线程同步机制(如原子)在进程共享内存中用于进程同步。至少,我认为很难设计一个不能跨进程正确工作的 std::atomic 特化的无锁实现。

于 2013-08-19T20:23:54.377 回答