Linux共享内存同步

10

我已经实现了两个应用程序,它们使用POSIX共享内存API(即shm_open)共享数据。一个进程更新存储在共享内存段中的数据,另一个进程读取它。我想使用某种互斥锁或信号量来同步访问共享内存区域。最有效的方法是什么?我正在考虑的一些机制包括:

  • 在共享内存段中存储的POSIX互斥锁(需要设置PTHREAD_PROCESS_SHARED属性)
  • 使用semget创建System V信号量

1
什么编程语言?对于C++,考虑使用boost::interprocess - Paul R
出于好奇,为什么您要使用共享内存段而不是在“tmpfs”或“hugetlbfs”上使用命名文件?我可能会使用POSIX互斥锁,因为它们在Linux上多年来一直非常快。 - tmyklebu
@tmyklebu:我正在使用共享内存来避免系统调用和不必要的数据复制。设置共享内存段后,只需使用指针即可访问数据。 - waffleman
当您在tmpfshugetlbfs上创建,增长和mmap()文件时,会发生哪些不必要的数据复制? - tmyklebu
4个回答

4

与其使用System V信号量,我会选择使用POSIX命名信号量,使用sem_open()等函数。


POSIX是否保证这样的信号量可以在进程之间共享?(看起来很合理,但我找不到措辞。)使用pshared为真的sem_init(http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html)肯定适用于此应用程序,因为他已经拥有了共享内存段。 - Nemo
使用POSIX信号量没有明显的开销,而且API非常简单直接。 - waffleman
要注意的是,信号量在POSIX中是一项可选功能。此外,有一些恶劣的实现方式会通过始终设置错误代码来“实现” sem_init。据我所知,OSX就是这样一个系统。 - Jens Gustedt

2

不妨把这当做一个答案。

你可以使用带有 pshared 参数的 sem_init 在共享内存空间中创建 POSIX 信号量。我过去成功地使用过它。

至于这是否比共享互斥锁和条件变量更快或更慢,只有通过分析才能得出结论。在 Linux 上,它们都依赖于 "futex" 机制,所以它们应该是相似的。


0
如果效率很重要,我会选择进程共享的互斥锁和条件变量。
据我所知,每个信号量操作都需要一个系统调用,因此未竞争的互斥锁应该比在类似互斥锁的情况下滥用的信号量更快。

1
通常情况下,sem_post 应该只是一个原子增量操作,而不会导致系统调用。 - Jens Gustedt

0

首先,真正的基准测试以了解性能是否重要。这些东西的成本经常被高估。因此,如果您发现对控制结构的访问与写入的数量级相同,只需采用在语义上最适合您的用例的任何构造即可。如果每次访问控制结构都写入了大约100个字节,则通常会出现这种情况。

否则,如果控制结构是瓶颈,您应该避免使用它们。 C11具有新概念的_Atomic类型和操作,可用于存在数据访问竞争的情况。 C11尚未广泛实施,但可能所有现代编译器都已经实现了这些功能的扩展。


C11和C++11的标准同步机制能在进程间工作吗?(标准中是否包含“跨进程”概念?) - Nemo
不,它们没有共享内存的概念。然而,所有现代处理器都必须提供此功能的底层处理器特性应该完全对此问题不加区分。它们直接作用于呈现给它们的内存。 - Jens Gustedt
不能保证标准机制在进程之间正确同步。例如,多线程环境可能使用用户空间线程库,而没有内核支持抢占。在这种情况下,同步原语可能不会通过适当的屏障同步内存。(不太可能?当然。但是标准允许这样做,因此这并不是对所提问题的良好答案。) - Nemo
我很难想象你如何确保线程之间的原子操作同步,而进程之间则不同。无论如何,你都需要管理线程之间的上下文切换。但是没错,你说的对,POSIX还没有时间针对C11做出反应。所有以前的C标准版本都通过直接引用被"合并"到了POSIX中。贫困的策略不是我的答案,而是始终强制使用并行处理的同步结构。自从多年以来,原子操作一直存在于CPU中,我们应该利用它们。 - Jens Gustedt
@Nemo,回到你所说的“糟糕答案”的话题。被接受的答案也不是更好,因为信号量是可选的 POSIX 特性,即使它们被实现,规范也过于薄弱,无法强制执行合理的行为。 - Jens Gustedt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接