第2章 快速上手
本章通过实战带你从零开始构建、配置、运行 iceoryx,并完成第一个 Publisher/Subscriber 示例。
2.1 环境准备
2.1.1 系统要求
最低配置
- 操作系统:Linux(内核 3.10+,推荐 4.x+)
- CPU:x86_64 或 ARM64
- 内存:2GB RAM
- 编译器:G++ 8.3+(推荐G+±9) 或 Clang 10+(C++17 支持)
- 构建工具:CMake 3.16+
推荐配置
- Ubuntu 20.04/22.04 或 Debian 11+
- 4 核以上 CPU
- 8GB RAM
- SSD 存储
2.1.2 依赖安装
bash
# Ubuntu/Debian
sudo apt update
sudo apt install -y \
build-essential \
cmake \
git \
libacl1-dev \
libncurses5-dev
# 可选:用于调试
sudo apt install -y gdb valgrind
2.2 构建 iceoryx
2.2.1 克隆源码
bash
git clone https://github.com/eclipse-iceoryx/iceoryx.git
cd iceoryx
2.2.2 配置构建选项
iceoryx 使用 iceoryx_meta 作为构建入口,这是一个元项目(meta project),用于统一构建所有组件:
iceoryx_platform:平台抽象层iceoryx_hoofs:基础工具库iceoryx_posh:核心通信库iceoryx_binding_c:C 语言绑定iceoryx_examples:示例代码
配置命令
bash
# 在 iceoryx 项目根目录执行
cmake -B build -S iceoryx_meta \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_SHARED_LIBS=ON \
-DEXAMPLES=ON \
-DINTROSPECTION=ON
注意 :早期版本的 CMake(< 3.13)可能需要使用
-H参数,但该参数已过时。现代 CMake 应使用-S指定源目录。
构建选项说明
CMAKE_BUILD_TYPE:Release(优化)或 Debug(调试符号)BUILD_SHARED_LIBS:ON(动态库)或 OFF(静态库)EXAMPLES:是否构建示例INTROSPECTION:是否构建监控工具
2.2.3 编译
bash
cmake --build build -j$(nproc)
编译完成后,产物位于 build/ 目录。主要组件结构如下:
text
build/
├── iox-roudi # RouDi 守护进程(核心)
├── hoofs/libiceoryx_hoofs.a # 基础工具库
├── platform/libiceoryx_platform.a # 平台抽象层
├── posh/ # 核心通信库及组件
│ ├── libiceoryx_posh.a
│ ├── libiceoryx_posh_roudi.a
│ ├── libiceoryx_posh_config.a
│ └── libiceoryx_posh_gateway.a
├── iceoryx_binding_c/libiceoryx_binding_c.a # C 语言绑定
├── iceoryx_introspection/ # 系统监控工具
│ └── iox-introspection-client
└── iceoryx_examples/ # 示例程序(18 类)
├── icehello/ # Hello World(最简单)
├── icedelivery/ # 基础发布订阅
├── icedelivery_in_c/ # C 语言版本
├── callbacks/ # 回调机制
├── callbacks_in_c/ # C 语言回调
├── waitset/ # 多路复用
├── waitset_in_c/ # C 语言 WaitSet
├── request_response/ # 请求-响应
├── request_response_in_c/ # C 语言请求-响应
├── icediscovery/ # 服务发现
├── iceperf/ # 性能测试
├── complexdata/ # 复杂数据类型
├── iceoptions/ # 配置选项
├── singleprocess/ # 单进程模式
├── user_header/ # 自定义头部
├── ice_access_control/ # 访问控制
└── experimental/ # 实验性特性
提示:iceoryx_examples 包含 18 个示例,涵盖从基础到高级的各种使用场景。这里先初步有个认识,后续章节将详细讲解这些示例的原理和用法。
2.2.4 安装(可选)
bash
sudo cmake --install build --prefix /usr/local
或安装到用户目录:
bash
cmake --install build --prefix ~/.local
export CMAKE_PREFIX_PATH=$HOME/.local:$CMAKE_PREFIX_PATH
2.3 启动 RouDi
2.3.1 基本启动
RouDi 是 iceoryx 的核心守护进程,必须先启动:
bash
./build/iox-roudi
预期输出
text
2025-xx-xx 01:01:51.949 [Info ]: No config file provided and also not found at '/etc/iceoryx/roudi_config.toml'. Falling back to built-in config.
2025-xx-xx 01:01:52.182 [Warn ]: IPC channel still there, doing an unlink of 'iox1_0_i_roudi'
2025-xx-xx 01:01:52.183 [Info ]: Resource prefix: iox1
2025-xx-xx 01:01:52.183 [Info ]: Domain ID: 0
2025-xx-xx 01:01:52.183 [Info ]: RouDi is ready for clients
说明:第一次运行可能会提示 IPC channel 清理(Warn 级别),这是正常的。RouDi 会自动清理之前的残留资源。
2.3.2 配置文件
默认情况下,RouDi 使用内置配置。可以通过 TOML 文件自定义内存池:
创建 roudi_config.toml:
toml
[general]
version = 1
[[segment]]
[[segment.mempool]]
size = 128
count = 10000
[[segment.mempool]]
size = 1024
count = 5000
[[segment.mempool]]
size = 16384
count = 1000
启动时指定配置:
bash
./build/iox-roudi -c roudi_config.toml
2.3.3 常见启动问题
问题 1:共享内存已存在
text
Error: Shared memory '/iceoryx_mgmt' already exists
解决方案:清理残留的共享内存
bash
rm -f /dev/shm/iceoryx_*
rm -f /dev/shm/iox_*
问题 2:权限不足
text
Error: Failed to create shared memory: Permission denied
解决方案 :检查 /dev/shm 权限或使用 sudo
bash
ls -ld /dev/shm
# 应该是 drwxrwxrwt
问题 3:内存不足
text
Error: Failed to allocate shared memory: Cannot allocate memory
解决方案:调整配置文件,减小内存池大小或数量
2.4 运行第一个示例:icehello
2.4.1 icehello 简介
icehello 是最简单的示例,演示基本的 Publisher/Subscriber 模式。
2.4.2 运行步骤
终端 1:启动 RouDi
bash
./build/iox-roudi
终端 2:启动订阅者
bash
./build/iceoryx_examples/icehello/iox-cpp-subscriber-helloworld
预期输出:
text
Waiting for: Radar.FrontLeft.Object ...
终端 3:启动发布者
bash
./build/iceoryx_examples/icehello/iox-cpp-publisher-helloworld
发布者输出:
text
Sending: 0
Sending: 1
Sending: 2
...
订阅者输出:
text
Received: 0
Received: 1
Received: 2
...
2.4.3 代码分析
发布者核心代码(简化)
cpp
#include "iceoryx_posh/popo/publisher.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"
int main()
{
// 初始化运行时,连接到 RouDi
iox::runtime::PoshRuntime::initRuntime("iox-cpp-publisher-helloworld");
// 创建发布者,三元组:Service/Instance/Event
iox::popo::Publisher<uint32_t> publisher({"Radar", "FrontLeft", "Object"});
// 提供服务
publisher.offer();
uint32_t counter = 0;
while (true) {
// 从内存池 loan 一个 Chunk
publisher.loan()
.and_then([&](auto& sample) {
// 直接在共享内存中写入数据
*sample = counter++;
std::cout << "Sending: " << *sample << std::endl;
// 发布到订阅者(零拷贝)
sample.publish();
})
.or_else([](auto& error) {
std::cerr << "Loan failed: " << error << std::endl;
});
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
订阅者核心代码(简化)
cpp
#include "iceoryx_posh/popo/subscriber.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"
int main()
{
iox::runtime::PoshRuntime::initRuntime("iox-cpp-subscriber-helloworld");
// 创建订阅者,匹配相同的三元组
iox::popo::Subscriber<uint32_t> subscriber({"Radar", "FrontLeft", "Object"});
// 订阅服务
subscriber.subscribe();
while (true) {
// 尝试获取数据(无拷贝)
subscriber.take()
.and_then([](const auto& sample) {
// 直接读取共享内存数据
std::cout << "Received: " << *sample << std::endl;
// sample 析构时自动 release
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
2.4.4 关键概念
- 三元组 :
{Service, Instance, Event}唯一标识一个通道 - loan():从共享内存池借出一个 Chunk
- publish():将 Chunk 推送到所有订阅者队列
- take():从队列获取 Chunk(无拷贝)
- 自动释放:Sample 析构时自动 release 引用
2.5 监控与调试
2.5.1 使用 introspection 工具
bash
./build/iceoryx_introspection/iox-introspection-client
输出示例:
text
=== RouDi Introspection ===
Publishers:
Service: Radar, Instance: FrontLeft, Event: Object
State: OFFERED
Subscribers: 1
Subscribers:
Service: Radar, Instance: FrontLeft, Event: Object
State: SUBSCRIBED
Queue: 0/256
2.5.2 查看共享内存
bash
ls -lh /dev/shm/iceoryx_*
2.5.3 调试技巧
启用 DEBUG 日志
bash
export IOX_LOG_LEVEL=DEBUG
./build/iox-roudi
使用 GDB 调试
bash
gdb ./build/iceoryx_examples/icehello/iox-cpp-publisher-helloworld
(gdb) break main
(gdb) run
(gdb) next
# 或者在具体源文件行设置断点
(gdb) break iox_publisher_helloworld.cpp:45
提示 :由于
loan()是模板函数,直接设置断点可能不生效。建议在 main 函数或具体源文件的行号设置断点。
2.6 进阶示例:icedelivery
icedelivery 示例演示更完整的功能,包括自定义数据类型。
2.6.1 运行 icedelivery
bash
# 终端 1
./build/iox-roudi
# 终端 2
./build/iceoryx_examples/icedelivery/iox-cpp-subscriber
# 终端 3
./build/iceoryx_examples/icedelivery/iox-cpp-publisher
2.6.2 自定义数据类型
cpp
struct RadarObject {
float x;
float y;
float velocity;
};
iox::popo::Publisher<RadarObject> publisher({"Radar", "Front", "Objects"});
publisher.loan()
.and_then([](auto& sample) {
sample->x = 10.5f;
sample->y = 20.3f;
sample->velocity = 5.2f;
sample.publish();
});
2.7 常见问题排查
2.7.1 连接问题
现象:订阅者收不到数据
排查步骤:
- 确认 RouDi 正在运行
- 检查三元组是否完全匹配
- 确认发布者调用了
offer() - 确认订阅者调用了
subscribe() - 查看 introspection 输出
2.7.2 性能问题
现象:延迟高或吞吐低
排查步骤:
- 确认使用 Release 构建(
-DCMAKE_BUILD_TYPE=Release) - 调整内存池配置(避免频繁失败重试)
- 设置 CPU 亲和性
cpp
iox::platform::IoxPthread::setThreadAffinity(pthread_self(), 0);
2.7.3 内存泄漏
现象:内存持续增长
排查步骤:
- 确认所有 Sample 都被正确 release
- 避免持有 Sample 超过必要时间
- 使用 Valgrind 检测
bash
valgrind --leak-check=full ./build/iceoryx_examples/icehello/iox-cpp-publisher-helloworld
输出:
text
==2550862== Memcheck, a memory error detector
==2550862== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2550862== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2550862== Command: ./build/iceoryx_examples/icehello/iox-cpp-publisher-helloworld
==2550862==
2025-12-04 03:15:54.035 [Warn ]: IPC channel still there, doing an unlink of 'iox1_0_u_iox-cpp-publisher-helloworld'
2025-12-04 03:15:56.228 [Info ]: Domain ID: 0
iox-cpp-publisher-helloworld sent value: 1
iox-cpp-publisher-helloworld sent value: 2
iox-cpp-publisher-helloworld sent value: 3
iox-cpp-publisher-helloworld sent value: 4
iox-cpp-publisher-helloworld sent value: 5
iox-cpp-publisher-helloworld sent value: 6
iox-cpp-publisher-helloworld sent value: 7
iox-cpp-publisher-helloworld sent value: 8
^C==2550862==
==2550862== HEAP SUMMARY:
==2550862== in use at exit: 1,812 bytes in 5 blocks
==2550862== total heap usage: 10,994 allocs, 10,989 frees, 28,974,390 bytes allocated
==2550862==
==2550862== LEAK SUMMARY:
==2550862== definitely lost: 0 bytes in 0 blocks
==2550862== indirectly lost: 0 bytes in 0 blocks
==2550862== possibly lost: 0 bytes in 0 blocks
==2550862== still reachable: 1,812 bytes in 5 blocks
==2550862== suppressed: 0 bytes in 0 blocks
==2550862== Reachable blocks (those to which a pointer was found) are not shown.
==2550862== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==2550862==
==2550862== For lists of detected and suppressed errors, rerun with: -s
==2550862== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
2.8 下一步
完成本章后,你应该能够:
- ✅ 构建并安装 iceoryx
- ✅ 启动 RouDi 并配置内存池
- ✅ 运行基本的 Publisher/Subscriber 示例
- ✅ 使用 introspection 工具监控系统
- ✅ 排查常见问题
后续章节预告:
- 第3章:深入架构,理解三平面设计与组件交互
- 第4章:内存管理细节,Chunk 生命周期与分配策略
- 第5章:高级特性,WaitSet、Callbacks、Request-Response