1. Iceoryx 架构
1.1 本节概述
本节简述 Eclipse Iceoryx 的架构和设计原理。
原文地址: concepts/architecture.md
1.2 软件层
Eclipse iceoryx 由以下主要包组成:
接下来的章节将逐一简要描述这些组件及其库。
1.3 组件和库
下图展示了不同的库及其命名空间:
1.3.1 Iceoryx hoofs
Handy Objects Optimized For Safety(hoofs)是一个库,包含各种构建块,如固定大小的容器、并发类和来自即将发布的 C++ 标准的新一代 C++ 构造。
有关组件的更多信息,请参阅其详细描述。
1.3.2 Iceoryx posh
iceoryx_posh
(POSIX SHared memory)包包含与共享内存进程间通信相关的所有内容。
核心库
这一部分描述了核心库的命名空间。
popo
:命名空间 posh ports 包含用于数据传输的用户 API 类。capro
:命名空间 canonical protocol 实现了标准协议模式协议,iceoryx_posh
使用该协议来连接和发现popo::Publisher
和popo::Server
。mepoo
:命名空间 memory pool 包含所有与内存相关的类,例如MemoryManager
或SharedPointer
。version
:命名空间 version 包含 ABI 兼容性检查。build
:命名空间 build 包含在编译前可以更改的某些最大值。
网关库
- 网关库及其命名空间
gw
包含创建网关的通用抽象。这些抽象由iceoryx_dds
使用。
RouDi 库
- RouDi 库及其命名空间
roudi
包含 RouDi 中间件守护进程使用的类。
1.3.3 Iceoryx C 绑定
模块 iceoryx_binding_c
使 iceoryx_posh
的进程间通信功能在 C 中可用。
1.3.4 Iceoryx DDS
iceoryx_dds
包提供了一个双向 DDS 网关,使用 Eclipse Cyclone DDS。该网关可用于通过网络(例如以太网)发送数据。
有关更多信息,请参阅Readme。
1.3.5 Iceoryx introspection
内省客户端可用于实时调试,并提供当前系统的信息,如内存使用情况和已建立的连接。
有关更多信息,请参阅 icecrystal 示例。
2. Iceoryx 目标与非目标
2.1 目标
- 为各种操作系统提供高性能的进程间通信
- 安全且灵活的API
- 动态连接的服务发现功能
- 对数据无关,不限制负载数据
- 兼容AUTOSAR Adaptive和ROS 2通信模式
- 提供构建网络协议网关的基础模块
- 汽车级软件质量
- 现代C++
2.2 非目标
- 提供数据模型和类似IDL或代码生成器的工具
- 缩小到可以运行在微控制器上(例如,内存小于1MB)
- 完全符合DDS标准
3 常见问题解答
本小节翻译自:website/FAQ.md
3.1 Iceoryx能在Docker环境中运行吗?
可以。请参阅icedocker示例。
3.2 Iceoryx在Docker环境中保留共享内存时崩溃并显示SIGABRT
检查Docker容器的--shm-size
标志。容器是否提供了所需的共享内存大小?
3.3 如何判断RouDi是否在运行?
RouDi使用文件锁机制确保一次只运行一个RouDi实例。为此,RouDi会创建并锁定/tmp/roudi.lock
。即使RouDi未运行,该文件也会存在。尝试锁定此文件,如果失败,则说明RouDi正在运行。
在命令行中执行:
flock -n /tmp/roudi.lock echo "RouDi is not running"
3.4 发布订阅场景中丢失样本
丢失样本对您的系统不可接受?发布者发送的频率是否高于订阅者接收样本的频率?
可以采取以下措施:
- 确保接收频率高于发布频率
或者
- 使用阻塞发布者功能
!!! 注意
使用阻塞发布者功能需要仔细考虑,因为在发布者被阻塞时,其他订阅者将无法接收样本。
另一种可能的解决方案是:
- 将
SubscriberOptions::queueCapacity
增加到最多256- 如果256不够,通过CMake开关增加最大值
IOX_MAX_CHUNKS_HELD_PER_SUBSCRIBER_SIMULTANEOUSLY
- 如果256不够,通过CMake开关增加最大值
3.5 使用Listener时丢失样本
如果订阅者与Listener结合使用,有些样本可能会在接收队列中等待被取走。Listener
使用事件,比WaitSet
的状态快,但这也意味着如果发布者在订阅者执行onSampleReceivedCallback
时触发了5个事件,它只会在离开回调后触发一次。如果您不注意从队列中取出所有数据,它们将一直留在队列中并填满队列。默认队列大小为256个样本。这意味着在onSampleReceivedCallback
中需要循环取出样本,直到take
报告队列为空。或者,可以使用WaitSet
代替Listener
。WaitSet
支持状态和事件,如果使用状态,它会在队列中有数据时触发。
3.6 解决错误MEPOO__MEMPOOL_GETCHUNK_POOL_IS_RUNNING_OUT_OF_CHUNKS
可能的解决方案包括以下几种:
- 增加RouDi的内存配置
- 确保接收频率高于发布频率
- 减少
SubscriberOptions::queueCapacity
,在订阅者一侧的内存池中保存较少的样本 - 考虑使用阻塞发布者功能。使用该功能需要仔细考虑,因为在发布者被阻塞时,其他订阅者将无法接收样本。
!!! 注意
使用阻塞发布者功能需要仔细考虑,因为在发布者被阻塞时,其他订阅者将无法接收样本。
3.7 iox-roudi启动失败
错误信息如下:
user@iceoryx-host:/# iox-roudi
Log level set to: [Warning]
SharedMemory still there, doing an unlink of /iceoryx_mgmt
Reserving 59736448 bytes in the shared memory [/iceoryx_mgmt]
[ Reserving shared memory successful ]
SharedMemory still there, doing an unlink of /root
Reserving 27902400 bytes in the shared memory [/root]
While setting the acquired shared memory to zero a fatal SIGBUS signal appeared
caused by memset. The shared memory object with the following properties
[ name = /root, sizeInBytes = 27902400, access mode = AccessMode::READ_WRITE,
ownership = OwnerShip::MINE, baseAddressHint = (nil), permissions = 0 ] maybe
requires more memory than it is currently available in the system.
表示没有足够的共享内存。检查:
df -H /dev/shm
查看是否有足够的内存可用。在此示例错误信息中,我们需要59736448
(iceoryx管理数据)+ 27902400
(用户样本)约83.57mb
的共享内存。
3.8 栈大小处理
Iceoryx中间件利用系统的栈内存来记录内部结构。大多数Linux发行版为每个进程提供8MB的栈内存,这对Iceoryx来说已经足够。您可以通过ulimit -a
的输出进行检查。
在其他平台(如Windows)上,栈内存有不同的规则。在Windows上,栈内存只有1MB。一般来说,不推荐在Iceoryx中增加栈大小,因为RouDi
可能会消耗大量内存而未使用它。尤其是在多线程环境中使用RouDi时,可能会耗尽栈内存并导致内存错误。
Single process
示例显示了在Windows上编译和执行时的情况。如果不设置栈大小,应用程序将在进入main()
方法时抛出Stack overflow
异常。
可以通过在CMake中添加链接器标志解决此问题:
target_link_options(single_process BEFORE PRIVATE /STACK:3500000)
对于其他平台,适用其他标志或解决方案。
CI失败但本地无法复现错误
可以使用tools/scripts/ice_env.sh
创建一个Iceoryx开发环境,其配置与CI目标非常相似。例如,如果目标ubuntu 18.04失败,可以创建一个docker容器:
cd tools/scripts
./ice-env.sh enter ubuntu:18.04
这将启动容器,安装Iceoryx所需的所有依赖项并进入环境。
Docker
在Docker环境中,检查是否有足够的内存可用:
# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
367b9fae6c2f nifty_galileo 0.00% 4.48MiB / 1GiB 0.44% 11.6kB / 0B 17.6MB / 0B 1
如果内存不足,可以使用--shm-size="2g"
标志重新启动docker容器,以增加可用的共享内存总量。
docker run -it --shm-size="2g" ubuntu
3.9 RouDi和进程的终止
为了避免Iceoryx posh的未定义行为,建议使用SIGINT或SIGTERM终止RouDi和相应的中间件进程。在RouDi中,我们集成了一个sighandler来捕获信号并使RouDi有机会退出并清理一切。这也适用于进程。因此,建议在进程中添加一个signalhandler(见此示例)。
3.10 如何在Bazel中使用Iceoryx作为外部依赖项
在您的WORKSPACE中定义Iceoryx仓库信息,然后调用load_repositories.bzl和setup_repositories.bzl中的Bazel宏来加载传递依赖项。
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
IOX_COMMIT = "....."
http_archive(
name = "eclipse_iceoryx",
sha256 = <sha256 sum of z>,
strip_prefix = "iceoryx-" + IOX_COMMIT,
url = "https://github.com/eclipse-iceoryx/iceoryx/archive/" + IOX_COMMIT + ".zip",
)
# 加载Iceoryx的传递依赖项
load("@eclipse_iceoryx//bazel:load_repositories.bzl", "load_repositories")
load("@eclipse_iceoryx//bazel:setup_repositories.bzl", "setup_repositories")
load_repositories()
setup_repositories()