Fast-DDS共享内存通信--初始化

一、概述

        DDS中数据的传输通过传输层实现,DDS定义了一个传输API,并且可以运行实现该API的插件;这样DDS就不局限于特定的传输,应用程序可以根据需要选择或实现适合需求的传输层插件。传输层位于Fast-DDS层级模型的最底层,传输层可以使用多种不同的传输协议。Fast-DDS具有五种已实现的传输插件,分别是UDPv4、UDPv6、TCPv4、TCPv6和SHM。

 Fast-DDS层级模型架构图

二、共享内存通信初始化

Fast-DDS共享内存通信初始化流程 

关键概念:

SharedMemoryManager:通过实例化SharedMemoryManager对象,应用程序可以访问内部维护的共享内存资源。每个进程至少需要一个内存管理器。SharedMemoryManager为应用程序提供了创建共享内存段、在段中分配数据缓冲区、将缓冲区描述符推送到共享内存端口以及创建与端口相关联的监听器的功能;

Segment:持有一个固定大小的内存块,可以被不同进程访问;共享内存段有一个全局名称,任何知道该名称的进程都可以打开该segment并将其映射到其地址空间中;

SegmentId:唯一标识共享内存段的名称,由16个字符的UUID描述;

SharedMemBuffer:继承自Buffer,在共享内存段中分配的缓存区;

BufferNode:缓存区描述符,这些描述符就像指向缓存区的指针,可以在进程之间以最小的成本进行复制,描述符包含SegmentId和从段的底部到数据的偏移量offset;

Port:由port_id标识的通信信道,通过该通道,缓存区描述符可以被发送到其他进程。

三、主要流程分析

3.1 SharedMemTransport初始化

bool SharedMemTransport::init()

        SharedMemTransport是Fast-DDS共享内存通信的一个实现,该类继承并实现Fast-DDS定义的传输API TransportInterface。在SharedMemTransport初始化时会通过SharedMemManager创建一个SharedMemManager实例,然后通过该实例进一步创建一个Segment实例;Segment持有一块用于进程间通信的固定大小的共享内存段。

bool SharedMemTransport::init()
{
    // TODO(Adolfo): Calculate this value from UDP sockets buffers size.
    static constexpr uint32_t shm_default_segment_size = 512 * 1024;

    if (configuration_.segment_size() == 0)
    {
        configuration_.segment_size(shm_default_segment_size);
    }

    if (configuration_.segment_size() < configuration_.max_message_size())
    {
        logError(RTPS_MSG_OUT, "max_message_size cannot be greater than segment_size");
        return false;
    }

    try
    {
        shared_mem_manager_ = SharedMemManager::create(SHM_MANAGER_DOMAIN);
        shared_mem_segment_ = shared_mem_manager_->create_segment(configuration_.segment_size(),
                        configuration_.port_queue_capacity());

        // Memset the whole segment to zero in order to force physical map of the buffer
        auto buffer = shared_mem_segment_->alloc_buffer(configuration_.segment_size(),
                        (std::chrono::steady_clock::now() + std::chrono::milliseconds(100)));
        memset(buffer->data(), 0, configuration_.segment_size());
        buffer.reset();

        if (!configuration_.rtps_dump_file().empty())
        {
            auto packets_file_consumer = std::unique_ptr<SHMPacketFileConsumer>(
                new SHMPacketFileConsumer(configuration_.rtps_dump_file()));

            packet_logger_ = std::make_shared<PacketsLog<SHMPacketFileConsumer>>();
            packet_logger_->RegisterConsumer(std::move(packets_file_consumer));
        }
    }
    catch (std::exception& e)
    {
        logError(RTPS_MSG_OUT, e.what());
        return false;
    }

    return true;
}

创建SharedMemTransport实例

        通过SharedMemManager的静态成员函数create()可以创建一个SharedMemManager实例,create()的实现如下:

    static std::shared_ptr<SharedMemManager> create(
            const std::string& domain_name)
    {
        return std::shared_ptr<SharedMemManager>(new SharedMemManager(domain_name));
    }

        构造SharedMemManager对象时会先对传入的domain_name进行校验,然后计算共享内存中用于存储共享内存描述符的额外的空间。

    SharedMemManager(
            const std::string& domain_name)
        : segments_mem_(0)
        , global_segment_(domain_name)
        , watch_task_(SegmentWrapper::WatchTask::get())
    {
        static_assert(std::alignment_of<BufferNode>::value % 8 == 0, "SharedMemManager::BufferNode bad alignment");

        if (domain_name.length() > SharedMemGlobal::MAX_DOMAIN_NAME_LENGTH)
        {
            throw std::runtime_error(
                      domain_name +
                      " too long for domain name (max " +
                      std::to_string(SharedMemGlobal::MAX_DOMAIN_NAME_LENGTH) +
                      " characters");
        }

        per_allocation_extra_size_ =
                SharedMemSegment::compute_per_allocation_extra_size(std::alignment_of<BufferNode>::value,
                        domain_name);
    }

创建Segment实例

        通过SharedMemManager实例的create_segment()方法可以创建一个持有一块儿固定大小共享内存的Segment实例保存在SharedMemTransport对象的成员变量shared_mem_segment_中,共享内存的创建便发生在Segment构造时。

    std::shared_ptr<Segment> create_segment(
            uint32_t size,
            uint32_t max_allocations)
    {
        return std::make_shared<Segment>(size + segment_allocation_extra_size(max_allocations), size, max_allocations,
                       global_segment_.domain_name());
    }

        注意,创建的共享内存的大小除了我们配置的大小外还需要额外的空间保存共享内存的描述符,在传输时只需要拷贝描述符来告诉其他进程数据在共享内存中的存储位置。

        Segment(
                uint32_t size,
                uint32_t payload_size,
                uint32_t max_allocations,
                const std::string& domain_name)
            : segment_id_()
            , overflows_count_(0)
        {
            generate_segment_id_and_name(domain_name);

            SharedMemSegment::remove(segment_name_.c_str());

            try
            {
                segment_ = std::unique_ptr<SharedMemSegment>(
                    new SharedMemSegment(boost::interprocess::create_only, segment_name_.c_str(), size));
                
                logInfo(RTPS_TRANSPORT_SHM, "malloc a shared memory with tag: "<<segment_name_<<", and size of this memory is "<<size<<", payload size: "<<payload_size);
            }
            catch (const std::exception& e)
            {
                logError(RTPS_TRANSPORT_SHM, "Failed to create segment " << segment_name_
                                                                         << ": " << e.what());

                throw;
            }

            free_bytes_ = payload_size;

            // Alloc the buffer nodes
            logInfo(RTPS_TRANSPORT_SHM, "allocate shared memory with tag "<<segment_name_<<", and size of BufferNode is "<<max_allocations);
            auto buffers_nodes = segment_->get().construct<BufferNode>
                        (boost::interprocess::anonymous_instance)[max_allocations]();

            // All buffer nodes are free
            for (uint32_t i = 0; i < max_allocations; i++)
            {
                buffers_nodes[i].status.exchange({0, 0, 0});
                buffers_nodes[i].data_size = 0;
                buffers_nodes[i].data_offset = 0;
                free_buffers_.push_back(&buffers_nodes[i]);
            }
        }

初始化共享内存段

        通过shared_mem_segment_从共享内存中申请内存并通过memset初始化

        // Memset the whole segment to zero in order to force physical map of the buffer
        auto buffer = shared_mem_segment_->alloc_buffer(configuration_.segment_size(),
                        (std::chrono::steady_clock::now() + std::chrono::milliseconds(100)));
        memset(buffer->data(), 0, configuration_.segment_size());
        buffer.reset();

四、参考链接

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Fast-DDS是一个高性能的、基于数据发布-订阅模型的通信协议。它提供了丰富的QoS(服务质量)策略以及灵活的路由机制,以满足不同场景下的通信需求。 其中,QoS策略是指在数据传输的过程中,可以通过一定的配置方式来控制数据传输的质量、可靠性、延时等因素。Fast-DDS中提供了多种QoS策略,包括消息传输的可靠性、数据的存储方式、订阅者优先级等。 在Fast-DDS中,消息传输的可靠性可以通过以下几种QoS策略来控制: 1. RELIABILITY,即可靠性:用于控制消息的可靠性,包括发布者和订阅者之间的ack确认机制,以及网络连接中的重传机制。 2. DURABILITY,即持久性:用于控制消息的持久化方式,包括将消息写入本地磁盘或将消息存储在内存中。 3. HISTORY,即历史消息:用于控制订阅者在订阅之前是否可以获取历史消息,以及历史消息保存的方式(内存中、磁盘中、或通过文件传输)。 除了上述QoS策略,Fast-DDS还提供了很多其他的QoS选项,如数据传输的优先级、最大传输延迟、数据分发的方式等等,以满足不同的通信场景需求。 总体而言,Fast-DDS的QoS策略提供了丰富的选项和灵活的配置方式,能够帮助开发人员实现高性能、可靠的数据传输。 ### 回答2: Fast-DDS是一种高性能的DDS实现,可以用于构建实时和分布式系统。Fast-DDS支持多种QoS策略,这些策略允许用户在系统性能和资源利用率之间做出权衡。以下是Fast-DDS的QoS策略的几个关键点: 1. 可靠性:Fast-DDS支持可靠和非可靠的通信,通过配置可靠性参数,可以控制数据重传和丢失时间等方面的行为。 2. 带宽控制:Fast-DDS支持带宽限制,通过配置带宽参数,可以限制系统的带宽使用,从而避免因过度使用带宽导致的卡顿和延迟。 3. 时间同步:Fast-DDS支持时间同步,通过配置时间同步参数,可以在系统中确立一个统一的时间基准,从而避免因时间差异而导致的不一致性。 4. 优先级:Fast-DDS支持消息优先级,通过配置优先级参数,可以确保重要消息的优先传输,从而提高系统的响应能力。 5. QoS Inheritance: Fast-DDS支持QoS继承,它允许用户在不同级别的实体之间继承QoS属性。例如,可将主题级别的QoS继承到订阅者级别,从而确保所有订阅者都使用同一的QoS策略。 综上所述,Fast-DDS的QoS策略提供了一些非常有用的功能,可以帮助用户在不同的反应性和资源利用率需求之间做出权衡,从而优化系统的性能和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值