【iceoryx】iox_publisher_helloworld 2

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

创建

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

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

template <typename port_t>
inline BasePublisher<port_t>::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<cxx::Serialization>(service).toString() << publisherOptions.serialize().toString()
               << static_cast<cxx::Serialization>(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<MemberType_t* const> publisherPortDataPtr) noexcept
    : BasePort(publisherPortDataPtr)
    , m_chunkSender(&getMembers()->m_chunkSenderData)

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

类的包含关系如下:
在这里插入图片描述

publisher :Loan()

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

注意 T = RadarObject,H = NoUserHeader。

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

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

cxx::expected<mepoo::ChunkHeader*, AllocationError>
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>(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<mepoo::ChunkHeader*>(chunk.getChunkHeader());
      }

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

inline Sample<T, H>
PublisherImpl<T, H, BasePublisherType>::convertChunkHeaderToSample(mepoo::ChunkHeader* const header) noexcept
{
    return Sample<T, H>(cxx::unique_ptr<T>(reinterpret_cast<T*>(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,H>,其中 T = RadarObject。一个指向userPayload 的unique_ptr,与 *this = PublisherImpl 传入Sample的构造。
在这里插入图片描述

SmartChunk<PublisherInterface<T, H>, T, H>; // T = RadarObject 
template <typename TransmissionInterface, typename T, typename H>
template <typename S, typename>
inline SmartChunk<TransmissionInterface, T, H>::SmartChunk(cxx::unique_ptr<T>&& 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<T, H>::publish() noexcept
{
    if (BaseType::m_members.smartChunkUniquePtr)
    {
        BaseType::m_members.producerRef.get().publish(std::move(*(this)));
    }
}

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

inline void PublisherImpl<T, H, BasePublisherType>::publish(Sample<T, H>&& 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、填充内容,发布。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个iceoryx发布订阅的C代码示例,其中一个发布者发布一个消息,而两个订阅者订阅这个消息: ```c #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include "iceoryx_posh/popo/subscriber.hpp" #include "iceoryx_posh/popo/publisher.hpp" #include "iceoryx_posh/runtime/posh_runtime.hpp" #include "iceoryx_posh/roudi/introspection_types.hpp" int main() { // 初始化iceoryx runtime iox::runtime::PoshRuntime::initRuntime("publisher"); // 创建发布者和订阅者 iox::popo::Publisher publisher({"Radar", "FrontLeft", "Object"}); iox::popo::Subscriber subscriber1({"Radar", "FrontLeft", "Object"}); iox::popo::Subscriber subscriber2({"Radar", "FrontLeft", "Object"}); // 订阅者1等待消息 subscriber1.subscribe(); printf("Subscriber 1 waiting for messages...\n"); // 订阅者2等待消息 subscriber2.subscribe(); printf("Subscriber 2 waiting for messages...\n"); // 发布者发布消息 printf("Publisher publishing message...\n"); publisher.publish("Hello, world!"); // 等待订阅者接收消息 while (true) { if (subscriber1.hasData()) { printf("Subscriber 1 received message: %s\n", subscriber1.getChunk()->userPayload()); break; } if (subscriber2.hasData()) { printf("Subscriber 2 received message: %s\n", subscriber2.getChunk()->userPayload()); break; } } // 清理资源并退出 iox::runtime::PoshRuntime::shutdownRuntime(); return 0; } ``` 需要注意的是,这个示例代码需要使用iceoryx库,需要将iceoryx库链接到您的项目中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值