共享内存或mmap - Linux C/C++ IPC

Hum*_*ger 15 c c++ ipc mmap shared-memory

上下文是进程间通信,其中一个进程("服务器")必须将固定大小的结构发送到在同一台机器上运行的许多侦听进程("客户端").

在Socket Programming中我很自在.为了使服务器和客户端之间的通信更快并减少副本数量,我想尝试使用共享内存(shm)或mmaps.

操作系统是RHEL 64位.

由于我是新手,请建议我应该使用哪个.如果有人能指点我一本书或在线资源来学习同样的东西,我会很感激.

谢谢你的回答.我想补充一点,服务器(市场数据服务器)通常会接收多播数据,这将导致它每秒向"客户端""发送"大约200,000个结构,其中每个结构大约为100字节.shm_open/mmap的实现是否仅对大型数据块或大量小型结构的性能优于套接字?

Jen*_*edt 23

我将mmapshm_open共享内存映射到进程的虚拟地址空间.这是相对直接和干净的:

  • 您可以使用某种符号名称来标识共享内存段,例如 "/myRegion"
  • shm_open您在该区域上打开文件描述符
  • 随着ftruncate你扩大细分到你需要的大小
  • mmap您一起将其映射到您的地址空间

shmat和Co接口有(至少在历史上),他们可能有该内存可以映射的最大数量的限制的缺点.

然后,所有的POSIX线程同步工具(pthread_mutex_t,pthread_cond_t,sem_t,pthread_rwlock_t,...)有初始化的接口,使您可以在一个进程共享环境中使用他们.所有现代Linux发行版都支持这一点.

这是否比套接字更可取?性能方面它可能会有所不同,因为你不必复制东西.但我想的主要观点是,一旦你初始化了你的片段,这在概念上就更简单了.要访问项目,您只需要锁定共享锁,读取数据然后再次解锁.

正如@R建议的那样,如果你有多个读者pthread_rwlock_t可能会使用最好的锁结构.

  • 如果您只是在一端读取,则互斥锁是错误的同步原语.你应该使用rwlocks. (2认同)

peo*_*oro 7

我曾经使用共享内存段实现了IPC库; 这让我可以避免复制(而不是将数据从发送方内存复制到内核空间,然后从内核空间复制到接收方内存,我可以直接从发送方复制到接收方内存).

无论如何,结果并不像我预期的那样好:实际上共享一个内存段是一个非常昂贵的过程,因为重新映射TLB条目和所有其余的都非常昂贵.有关详细信息,请参阅此邮件(我不是那些人之一,但在开发我的库时会收到这样的邮件).

结果仅适用于非常大的消息(例如超过几兆字节),如果您使用的是小缓冲区,除非您愿意编写内核模块,否则unix套接字是您可以找到的最优化的东西.


dat*_*olf 7

除了已经提出的建议之外,我还想提供另一种方法:IPv6节点/接口本地多播,即受限于环回接口的多播. http://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xml#ipv6-multicast-addresses-1

起初这可能看起来很重,但大多数操作系统在零拷贝架构中实现环回套接字.映射到buf传递给的参数的页面send将被分配一个额外的映射并在写入时标记为复制,这样如果发送程序覆盖其中的数据,或者解除分配,则将保留内容.

您应该使用强大的数据结构,而不是传递原始结构.Netstrings http://cr.yp.to/proto/netstrings.txt和BSON http://bsonspec.org/浮现在脑海中.

  • 两种方式都做的次数比我想数的多,对我来说,在 IPC 的新项目上使用套接字就像甘道夫对进入莫里亚矿井的保留。你就是无法摆脱会遇到炎魔的感觉。如果您经常写入页面,则 COW 是重量级的,因为除了副本之外,您还会使 TLB 失效,正如 Linus 所说,“您完全属于那种糟糕的类别”。结构 + shmem = 简单和顶级性能,套接字 + 序列化 = 复杂且更慢。不知道为什么那么多人选择后者。 (2认同)