栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【iceoryx】iox

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【iceoryx】iox

上一节讲了initRuntime(APP_NAME),这节来讲publisher的创建、发布过程。

创建
struct RadarObject
{
    double x = 0.0;
    double y = 0.0;
    double z = 0.0;
};
Publisher publisher({"Radar", "FrontLeft", "Object"});

实例化一个Publisher的类,模板是RadarObject。追踪一下代码:

template 
inline BasePublisher::BasePublisher(const capro::ServiceDescription& service,
                                            const PublisherOptions& publisherOptions)
    : m_port(iox::runtime::PoshRuntime::getInstance().getMiddlewarePublisher(service, publisherOptions))

port_t = PublisherPortUser。m_port即PublisherPortUser类,即最终调了PublisherPortUser类的构造。

看一下PoshRuntimeImpl::getMiddlewarePublisher()这个接口。

PublisherPortUserType::MemberType_t*
PoshRuntimeImpl::getMiddlewarePublisher(const capro::ServiceDescription& service,
                                        const popo::PublisherOptions& publisherOptions,
                                        const PortConfigInfo& portConfigInfo) noexcept
{
    constexpr uint64_t MAX_HISTORY_CAPACITY =
        PublisherPortUserType::MemberType_t::ChunkSenderData_t::ChunkDistributorDataProperties_t::MAX_HISTORY_CAPACITY;

    auto options = publisherOptions;
...
    if (options.nodeName.empty())
    {
        options.nodeName = m_appName;
    }

    IpcMessage sendBuffer;
    sendBuffer << IpcMessageTypeToString(IpcMessageType::CREATE_PUBLISHER) << m_appName
               << static_cast(service).toString() << publisherOptions.serialize().toString()
               << static_cast(portConfigInfo).toString();

    auto maybePublisher = requestPublisherFromRoudi(sendBuffer);
...
    return maybePublisher.value();
}

PublisherPortUserType::MemberType_t为PublisherPortData类型。参数2,3为default。
该接口将一组序列化sendBuffer发送给RouDi,包括APP_NAME,service,publisherOptions,portConfigInfo。
并或取receiveBuffer的参数,生成一个PublisherPortData类型的指针。

发送用的 Interface

m_RoudiIpcInterface.send(msg)      //发送用的是 m_RoudiIpcInterface

接收用的 Interface

m_AppIpcInterface.receive(answer)  //接收用的是 m_AppIpcInterface

提取的参数为segmentId、offset,调用 getPtr 获取shared memory上存储chunk信息的位置,赋给ptr,然后强转为PublisherPortData * 类型。如下:

auto ptr = rp::BaseRelativePointer::getPtr(segmentId, offset);

PublisherPortUser构造的形参即PublisherPortData 类型,传给BasePort结构体转为了BasePortData *。

PublisherPortUser::PublisherPortUser(cxx::not_null publisherPortDataPtr) noexcept
    : BasePort(publisherPortDataPtr)
    , m_chunkSender(&getMembers()->m_chunkSenderData)

然后用BasePortData *这个指针构造了一个ChunkSender即m_chunkSender。最终给ChunkDistributor这个类的ChunkDistributorData *m_chunkDistrubutorDataPtr赋值。

类的包含关系如下:

publisher :Loan()
auto loanResult = publisher.loan();
template 
template 
inline cxx::expected, AllocationError>
PublisherImpl::loan(Args&&... args) noexcept
{
    return std::move(loanSample().and_then([&](auto& sample) { new (sample.get()) T(std::forward(args)...); }));
}

注意 T = RadarObject,H = NoUserHeader。

template 
inline cxx::expected, AllocationError> PublisherImpl::loanSample() noexcept
{
    static constexpr uint32_t USER_HEADER_SIZE{std::is_same::value ? 0U : sizeof(H)};
    auto result = port().tryAllocateChunk(sizeof(T), alignof(T), USER_HEADER_SIZE, alignof(H));
    return cxx::success>(convertChunkHeaderToSample(result.value()));    
}

port()返回的是m_port(PublisherPortUser类型),因此调了

cxx::expected
PublisherPortUser::tryAllocateChunk(const uint32_t userPayloadSize,
                                    const uint32_t userPayloadAlignment,
                                    const uint32_t userHeaderSize,
                                    const uint32_t userHeaderAlignment) noexcept
{
    return m_chunkSender.tryAllocate(
        getUniqueID(), userPayloadSize, userPayloadAlignment, userHeaderSize, userHeaderAlignment);
}

又调用了ChunkSender的tryAllocate(),这个接口相对复杂。基本操作如下:

1、 用传入的参数实例化了一个ChunkSettings类。

2、 判断上一次分配的chunk是否发送失败,如失败,则判断其大小是否合适,如合适则使用。

3、 如2未成功,则分配一个新的chunk:

auto getChunkResult = getMembers()->m_memoryMgr->getChunk(chunkSettings);

调用MemoryManager::getChunk,操作如下:

for (auto& memPool : m_memPoolVector)   //从m_memPoolVector这个vector中取出MemPool
    {
        uint32_t chunkSizeOfMemPool = memPool.getChunkSize(); //MemPool中chunk块大小是否合适
        if (chunkSizeOfMemPool >= requiredChunkSize)
        {
            chunk = memPool.getChunk();    //<1>
            memPoolPointer = &memPool;
            aquiredChunkSize = chunkSizeOfMemPool;
            break;
        }
    }
 auto chunkHeader = new (chunk) ChunkHeader(aquiredChunkSize, chunkSettings);
 auto chunkManagement = new (m_chunkManagementPool.front().getChunk())
            ChunkManagement(chunkHeader, memPoolPointer, &m_chunkManagementPool.front()); //<2>
 return cxx::success(SharedChunk(chunkManagement));

<1>处get了一块chunk,返回的是chunkheader的首地址。
<2>要重点分析一下:
看一下RouDi在创建内存池时最后一步操作:在 /dev/shm/iceoryx_mgmt 上划分了 m_totalNumberOfChunks 个 ChunkManagement 。

void MemoryManager::generateChunkManagementPool(posix::Allocator& managementAllocator) noexcept
{
    m_denyAddMemPool = true;
    uint32_t chunkSize = sizeof(ChunkManagement);
    m_chunkManagementPool.emplace_back(chunkSize, m_totalNumberOfChunks, managementAllocator, managementAllocator);
}

在publisher端,每获取一个chunk,同时获取一个ChunkManagement。初始化一个ChunkHeader,放在获取的chunk头部,
并作为参数初始化了一个ChunkManagement,然后构造了一个SharedChunk,返回。

if (getMembers()->m_chunksInUse.insert(chunk))
      {
          // END of critical section
          chunk.getChunkHeader()->setOriginId(originId);
          return cxx::success(chunk.getChunkHeader());
      }

将chunk插入m_chunksInUse链表,返回了一个chunkHeader *。

inline Sample
PublisherImpl::convertChunkHeaderToSample(mepoo::ChunkHeader* const header) noexcept
{
    return Sample(cxx::unique_ptr(reinterpret_cast(header->userPayload()),
                                           [this](auto* userPayload) {
                                               auto chunkHeader = iox::mepoo::ChunkHeader::fromUserPayload(userPayload);
                                               this->port().releaseChunk(chunkHeader);
                                           }),
                        *this);
}

这段代码 header->userPayload() 是获取 chunk的userPayload 段,即把ChunkHeader去掉。强转为 T* 类型指针。然后把ChunkHeader从UsedChunkList上remove掉。
返回一个Sample,其中 T = RadarObject。一个指向userPayload 的unique_ptr,与 *this = PublisherImpl 传入Sample的构造。

SmartChunk, T, H>; // T = RadarObject 
template 
template 
inline SmartChunk::SmartChunk(cxx::unique_ptr&& smartChunkUniquePtr,
                                                           TransmissionInterface& producer) noexcept
    : m_members({std::move(smartChunkUniquePtr), producer})
{
}

初始化了 SmartChunkPrivateData类型的m_members,其中producer = PublisherImpl ,smartChunkUniquePtr为RadarObject 类型指针。

至此 auto loanResult = publisher.loan()分析完毕。返回了一个 Sample 类型的实例。

下面分析publish():
auto& sample = loanResult.value();
// Sample can be held until ready to publish
sample->x = ct;
sample->y = ct;
sample->z = ct;
sample.publish();

这段操作比较迷惑,为甚么sample能这么赋值。原因是sample的父类重载了->。

void Sample::publish() noexcept
{
    if (BaseType::m_members.smartChunkUniquePtr)
    {
        BaseType::m_members.producerRef.get().publish(std::move(*(this)));
    }
}

reference_wrapper producerRef 可知,get()获取到的是PublisherImpl。

inline void PublisherImpl::publish(Sample&& sample) noexcept
{
    auto userPayload = sample.release(); // release the Samples ownership of the chunk before publishing
    auto chunkHeader = mepoo::ChunkHeader::fromUserPayload(userPayload);
    port().sendChunk(chunkHeader);
}

port()返回的是m_port(PublisherPortUser类型),最终调了deliverToAllStoredQueues(mepoo::SharedChunk chunk),主要进行了如下操作:

1 、从m_chunkDistrubutorDataPtr的m_queues获取substribute的queue

 for (auto& queue : getMembers()->m_queues) // 从m_chunkDistrubutorDataPtr的m_queues获取substribute的queue

2 、将chunk push进queue

if (pushToQueue(queue.get(), chunk)) // 将chunk push进queue

3、计数加1

 ++numberOfQueuesTheChunkWasDeliveredTo; //
总结:

1、实例化一个publisher ,将service、app_name等信息发送给RouDi,根据返回信息构建了一个 PublisherPortUser,该实例包含一个m_chunkSender,用于申请、发送chunk。

2、用loan()创建一个sample,实际申请了一块chunk,并返回其首地址。

3、填充内容,发布。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1019654.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号