0. 概述
本文简单介绍Iceoryx的共享内存通信机制。
1. 基础知识
当POSIX系统中的一个进程启动时,它会获得自己的虚拟地址空间。
虚拟地址空间的范围可能对不同的进程是相同的,但在特定地址上可访问的数据可能对每个进程都是不同的。
应用程序中的指针使用它运行所在进程的虚拟地址空间。
在进程的虚拟地址空间内,有许多“内存区域”用于加载或映射数据。这些内存区域通常是不连续的范围。一些内存区域的示例包括:
- 运行程序的执行指令(即程序的
.text
段) - 静态变量声明(即程序的
.data
段) - 运行程序使用的共享库的执行指令(库的
.text
段) - 进程的栈
- 进程的堆
- 共享内存段
共享内存段是对进程来说位于某个外部位置的物理内存(例如在RAM的某个部分或文件系统上),通过映射到其虚拟地址空间中的某个内存区域使其可访问。
一个共享内存段可以映射到多个进程,但是在不同进程中的映射地址可能(并且很可能会)不同。
POSIX API 提供了处理共享内存段的工具。
2. 组织结构
Iceoryx系统利用一个“管理”段进行管理目的,并使用任意数量的“用户”段在服务之间进行事件通信。
这些段在逻辑上被划分为“内存池”。内存池包含数量相同大小的“内存块”。
内存块是Iceoryx系统中用于共享内存访问的基本单元。
Iceoryx系统使用的段数量以及它们包含的内存池配置通过配置文件提供给系统。
配置可以在编译时(作为头文件)或运行时(作为toml格式的文本文件)提供。详见配置指南。
3. 零拷贝通信
通过连接的popo::Publisher
/popo::Subscriber
和popo::Server
/popo::Client
可以通过共享内存进行通信,实现零拷贝通信。在本节中,popo::Publisher
和popo::Server
将被称为生产者,而popo::Subscriber
和popo::Client
称为消费者。
生产者有一个分配的共享内存段,可以将数据写入其中。在POSIX系统中,这纯粹基于文件访问权限决定,因为内存段被表示为虚拟文件。
为了输出数据,生产者在其分配的内存段中预留一个内存块。Iceoryx系统将选择能够容纳输出数据结构的最小块大小。注意,即使数据类型小于块的大小,也会保留整个块。
生产者明确选择何时将写入内存块中的数据传递给所有已连接的消费者(通过发现建立)。当这种情况发生时,指向内存块的指针会放在消费者的接收队列中。消费者可以在方便的时候通过跟随指针访问数据。
消费者必须明确指示何时完成了对收到的特定内存块的处理。当所有已连接的消费者指示他们已完成时,内存块将返回到内存池。
3.1 关于指针的注意事项
如前所述,共享内存段可能会映射到进程虚拟地址空间中的不同内存区域。为了解决这个问题,Iceoryx使用了特殊的指针类型:iox::RelativePointer
和iox::relocatable_ptr
。
使用这些类型,在定位内存块时,内存映射的差异不是一个问题。
关于这些类型如何工作的详细讨论可以在这里找到。