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

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

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

操作系统是RHEL 64位。

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

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

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

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

shmat和Co接口(至少在历史上)具有以下缺点:它们可能限制您可以映射的最大内存量。

然后,所有POSIX线程同步工具( pthread_mutex_tpthread_cond_tsem_tpthread_rwlock_t ,…)都具有初始化接口,允许您在进程共享上下文中使用它们。 所有现代Linux发行版都支持这一点。

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

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

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

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

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

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

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

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

在POSIX shm_open/mmap接口和较旧的System V shmop之间进行选择不会产生很大的不同,因为在初始化系统调用之后,您最终会遇到相同的情况:在各个进程之间共享的内存区域。 如果您的系统支持它,我建议使用shm_open/mmap ,因为这是一个设计更好的界面。

然后,您可以将共享内存区域用作通用黑板,其中所有进程都可以对其数据进行分页。 困难的部分是同步访问该区域的进程。 在这里,我建议避免编制自己的同步方案,这可能非常困难且容易出错。 相反,使用现有的基于工作套接字的实现来同步进程之间的访问,并仅使用共享内存在进程之间传输大量数据。 即使使用这种方案,您也需要一个中央流程来协调缓冲区的分配,因此只有当您需要传输大量数据时,此方案才有价值。 或者,使用同步库,如Boost.Interprocess 。