文章目录
- 0. 引言
- 1. Iceoryx使用到的零拷贝技术
- 1.1 零拷贝技术概述
- 1.2 零拷贝的优势
- 1.3 Iceoryx零拷贝的实现
- 1.4 信息轮询与信号触发
- 2. Iceoryx的核心概念
- 3. Iceoryx使用示例
- 3.1 发布者程序
- 3.2 订阅者程序
- 3.3 编译和运行
- 3.4 压力测试脚本
- 4. 参考文章
0. 引言
Iceoryx是一个开源的实时通信框架,特别适用于需要高性能和低延迟的嵌入式系统,如自动驾驶系统、机器人控制、航空航天等。
详细介绍请看官网
iceoryx 使用真正的零拷贝共享内存方法,允许将数据从发布者传输到订阅者而无需任何副本。这可确保数据传输具有恒定的延迟,无论有效负载的大小是多少。
1. Iceoryx使用到的零拷贝技术
1.1 零拷贝技术概述
零拷贝是指在数据传输过程中,避免不必要的数据拷贝操作。传统的数据传输通常涉及将数据从一个缓冲区复制到另一个缓冲区,这会产生额外的开销。而零拷贝技术允许数据在不进行拷贝的情况下直接传递到目标缓冲区,从而提高传输效率。
Iceoryx使用共享内存进行进程间通信,将数据放置在共享内存区域,然后通过指针引用实现数据传递,避免了数据的额外复制。
1.2 零拷贝的优势
- 减少CPU开销:零拷贝减少了CPU的复制操作,提高了系统性能。
- 降低内存占用:由于数据不需要在不同缓冲区之间复制,内存使用更为高效。
- 降低传输延迟:数据直接传递,无需复制等待时间。
1.3 Iceoryx零拷贝的实现
Iceoryx通过以下方式实现真正的零拷贝:
- 利用共享内存技术,预先开辟内存块(chunk),publisher将数据写入。
- Subscriber通过指针获取chunk中的信息,数据被写入时,subscriber收到一个指针。
- Iceoryx维护每个chunk的引用记录,确保资源不被浪费。
1.4 信息轮询与信号触发
为了提升数据获取效率,Iceoryx提供两种方式:
- WaitSet:采用react设计模式,绑定对应的subscribers,数据到来时触发通知。
- Listener:直接触发用户定制的callback,数据到来时调用回调函数。
2. Iceoryx的核心概念
掌握以下核心概念可以帮助进行Iceoryx的基本通信功能开发:
-
RouDi:Iceoryx的守护进程,不同应用需要与之连接才能正常通信。运行Iceoryx程序前需先启动RouDi。
iox-roudi
-
Runtime:每个应用在Iceoryx框架下运行时需要初始化其runtime。
constexpr char APP_NAME[] = "iox-publisher"; iox::runtime::PoshRuntime::initRuntime(APP_NAME);
-
Publisher*:数据发送器,需要绑定topic使用。
iox::popo::Publisher<Data> publisher({"Group", "Topic", "Instance"});
-
Subscriber:数据接收器,需要绑定topic使用。
iox::popo::Subscriber<Data> subscriber({"Group", "Topic", "Instance"});
-
Topic:数据载体,Publisher发送一个Topic,Subscriber接收相应的Topic。
3. Iceoryx使用示例
3.1 发布者程序
#include "iceoryx_posh/popo/publisher.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"
#include <chrono>
#include <thread>
#include <iostream>struct Data {char message[128];
};int main() {constexpr char APP_NAME[] = "iox-publisher";iox::runtime::PoshRuntime::initRuntime(APP_NAME);iox::popo::Publisher<Data> publisher({"Group", "Topic", "Instance"});while (true) {publisher.loan().and_then([&](auto& sample) {std::strcpy(sample->message, "Hello from Publisher");sample.publish();}).or_else([](auto& error) {std::cerr << "Loaning sample failed: " << error << std::endl;});std::this_thread::sleep_for(std::chrono::milliseconds(1000));}return 0;
}
3.2 订阅者程序
#include "iceoryx_posh/popo/subscriber.hpp"
#include "iceoryx_posh/runtime/posh_runtime.hpp"
#include <chrono>
#include <thread>
#include <iostream>struct Data {char message[128];
};int main() {constexpr char APP_NAME[] = "iox-subscriber";iox::runtime::PoshRuntime::initRuntime(APP_NAME);iox::popo::Subscriber<Data> subscriber({"Group", "Topic", "Instance"});while (true) {subscriber.take().and_then([&](const auto& sample) {std::cout << "Received: " << sample->message << std::endl;}).or_else([](auto& error) {if (error != iox::popo::ChunkReceiveResult::NO_CHUNK_AVAILABLE) {std::cerr << "Taking sample failed: " << error << std::endl;}});std::this_thread::sleep_for(std::chrono::milliseconds(100));}return 0;
}
3.3 编译和运行
确保已安装Iceoryx,并正确配置CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(IceoryxPubSub)set(CMAKE_CXX_STANDARD 14)
find_package(iceoryx_posh REQUIRED)add_executable(publisher_iceoryx publisher_iceoryx.cpp)
target_link_libraries(publisher_iceoryx iceoryx_posh::iceoryx_posh iceoryx_posh::iceoryx_posh_roudi_environment)add_executable(subscriber_iceoryx subscriber_iceoryx.cpp)
target_link_libraries(subscriber_iceoryx iceoryx_posh::iceoryx_posh iceoryx_posh::iceoryx_posh_roudi_environment)
然后在项目根目录创建并运行CMake:
mkdir build
cd build
cmake ..
make
运行RouDi(Iceoryx的守护进程):
iox-roudi &
运行发布者和订阅者程序:
./publisher_iceoryx &
./subscriber_iceoryx &
3.4 压力测试脚本
#include <thread>
#include <vector>
#include <cstdlib>void run_publisher() {system("./publisher_iceoryx");
}void run_subscriber() {system("./subscriber_iceoryx");
}int main() {const int num_publishers = 10;const int num_subscribers = 10;std::vector<std::thread> threads;for (int i = 0; i < num_publishers; ++i) {threads.emplace_back(run_publisher);}for (int i = 0; i < num_subscribers; ++i) {threads.emplace_back(run_subscriber);}for (auto& t : threads) {t.join();}return 0;
}
4. 参考文章
iceoryx源码阅读
iceoryx_github