C++高性能通信:了解Iceoryx与零拷贝技术的实现与应用

0. 引言

Iceoryx是一个开源的实时通信框架,特别适用于需要高性能和低延迟的嵌入式系统,如自动驾驶系统、机器人控制、航空航天等。

详细介绍请看官网

img

iceoryx 使用真正的零拷贝共享内存方法,允许将数据从发布者传输到订阅者而无需任何副本。这可确保数据传输具有恒定的延迟,无论有效负载的大小是多少。

img

1. Iceoryx使用到的零拷贝技术

1.1 零拷贝技术概述

零拷贝是指在数据传输过程中,避免不必要的数据拷贝操作。传统的数据传输通常涉及将数据从一个缓冲区复制到另一个缓冲区,这会产生额外的开销。而零拷贝技术允许数据在不进行拷贝的情况下直接传递到目标缓冲区,从而提高传输效率。

Iceoryx使用共享内存进行进程间通信,将数据放置在共享内存区域,然后通过指针引用实现数据传递,避免了数据的额外复制。

1.2 零拷贝的优势

  1. 减少CPU开销:零拷贝减少了CPU的复制操作,提高了系统性能。
  2. 降低内存占用:由于数据不需要在不同缓冲区之间复制,内存使用更为高效。
  3. 降低传输延迟:数据直接传递,无需复制等待时间。

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

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值