当前位置: 首页 > news >正文

fastdds:传输层SHM和DATA-SHARING的区别

 下图是fastdds官方的图,清晰地展示了dds支持的传输层:

根据通信双方的相对位置(跨机器、同机器跨进程、同进程)的不同选择合适的传输层,是通信中间件必须要考虑的事情。

跨机器:udp、tcp

跨机器通信,只能通过网络, fastdds支持UDP和TCP。

同机器,跨进程:SHM、DATA-SHARING

在同一个机器中,使用UDP和TCP进行通信,当然也是可以的。但是,从性能的角度来考虑,更推荐使用SHM,SHM和UDP/TCP相比有2点优势:

1、减少系统调用次数

共享内存创建完成之后,后边使用的时候直接是内存操作,而UCP/TCP每次发送或者接收的的时候,都要通过系统调用。

2、支持超长消息

UDP/TCP受协议栈的影响,可能会对消息进行分片和组装。

SHM也不完全是优点,也有自己的缺点,比如实现复杂:

1、使用UDP/TCP来收发包,功能成熟,拿来就用。使用SHM进行通信的话,很多功能需要自己实现,比如进程间的同步(通过信号量来实现),buffer的管理(segment)等。

 

同进程:INTRA-PROCESS

如果writer和reader在同一个进程,那么可以使用这种传输方式,writer线程会直接调用reader的回调函数,在同一个线程中。

如下图所示,使用如下命令gdb --args ./delivery_mechanisms pubsub -m INTRA-PROCESS,并对函数PubSubApp::on_data_available设置断点,可以看到调用栈,使用INTRA-PROCESS的时候,subscriber的接收回调函数直接被发送线程调用,在同一个线程中。这是一种效率最高的方式,去掉了中间缓存的环节。

 

在默认情况下,在同进程的通信使用intra-process,同机器跨进程使用SHM,跨机器使用UDP。

从上图可以看出,同机器的不同进程间通信可以使用SHM,也可以使用DATA-SHARING,那么两者有什么区别呢?

本文使用fastdds中的例子delivery_mechanisms,这个例子可以测试不同的传输层。

本文先介绍SHM和DATA-SHARING传输层的数据收发流程,最后总结两者之间的异同。

1SHM收发流程

简单来说,如果要看SHM的发送流程,那么可以以函数SharedMemTransport::send为中心进行梳理,通过gdb对这个函数设置断点,可以查看这个函数的调用栈,阅读这个函数的代码,可以看这个函数的发送操作;如果要看SHM的接收过程,可以通过函数SharedMemChannelResource::perform_listen_operation来梳理,对于TCP,UDP,SHM来说,三种传输层都实现了自己的函数perform_listen_operation,这个函数监听接收数据。

上图中左边是TranportDescriptor,右边是Transport。用户在创建传输层的时候,并不需要直接创建一个Transport,而是构造一个传输层的描述符即可,fastdds内部根据描述符来创建传输层。这是典型的设计模式中的建造者模式,当构造一个对象时,如果需要传递较多的参数,并且参数之间还有一些依赖关系,那么构造函数实现起来就会比较复杂,这个时候就可以使用建造者模式,在建造者中对参数进行检查,如果没有问题则直接构造对象,让构造函数只专注于对象的构造,至于参数的检查工作,放到构造器中来完成。

上图中左图的SenderResource负责发送数据,右图的ChannelResource负责接收数据。

UDP、TCP和SHM的类有平行的关系,他们也有自己的TransportDescriptor、Tranport、SenderResource、ChannelResource类。 通过一个基类或者接口派生出3个传输层,体现了c++中多态的使用。

 

Locator用来描述一个端点,其中kind包括LOCATOR_KIND_TCPv4、LOCATOR_KIND_UDPv4、 LOCATOR_KIND_TCPv6、LOCATOR_KIND_UDPv6、LOCATOR_KIND_SHM;port就是通信的端口,比如7400、7410这些;address是地址,在UDP、TCP中就是ip地址。只要知道要发送对象的Locator信息,那么数据就可以发送出去。

Locator {int32_t kind;uint32_t port;std::string address;
}

1.1发送

下图是对SharedMemTransport::send设置断点,看到的调用栈。

发送流程:

①用户调用DataWriter::write进行发送

②在DataWriterHistory中,将要发送的数据封装到一个CacheChange_t中

③在发送侧,dds和rtps之间的接口类是dds侧的DataWriterHistory,rtps侧的WriterHistory

④rtps中的BaseWriter进行发送

⑤RtpsParticipantImpl::sendSync进行发送,SenderResource属于Participant中的资源,在Participant中调用SenderResource的send函数,最终调用到SharedMemTransport的send

 

然后我们再来看看SharedMemTransport::send函数内部的实现:

bool SharedMemTransport::send(const std::vector<NetworkBuffer>& buffers,uint32_t total_bytes,fastdds::rtps::LocatorsIterator* destination_locators_begin,fastdds::rtps::LocatorsIterator* destination_locators_end,const std::chrono::steady_clock::time_point& max_blocking_time_point)
{...fastdds::rtps::LocatorsIterator& it = *destination_locators_begin;bool ret = true;std::shared_ptr<SharedMemManager::Buffer> shared_buffer;try{while (it != *destination_locators_end){if (IsLocatorSupported(*it)){if (shared_buffer == nullptr){shared_buffer = copy_to_shared_buffer(buffers, total_bytes, max_blocking_time_point);}ret &= send(shared_buffer, *it);...}++it;}}...return ret;}

①对于第一次循环,需要通过函数copy_to_shared_buffer将数据保存到共享内存中

在函数copy_to_shared_buffer中打印shared_mem_segment_的信息,可以看到segment name是fastdds_aec0cadd732cebe2,这个就是发送数据要拷贝的共享内存。所以说,使用共享内存发送数据时,会进行一次数据拷贝。

 

将数据拷贝到共享内存之后,就是通知接收者,通知接收者需要两个步骤来实现:

①构造一个BufferDescrptor(简称BD),并将BD发送到对应的端口

②环形监听者

构造一个BufferDescriptor:

唤醒监听者:

1.2接收

对Subscriber的接收回调设置断点,调用栈如下:

2DATA-SHARING收发流程

DATA-SHARING不是一个标准的传输层,没有TransportDescriptor、Transport、SenderResource、ChannelResource这些资源。

2.1发送

delivery mechanisms这个例子,publish的时候,并不是构造了一个临时的变量发送出去的,而是通过load_sample首先从底层申请了一块内存,而这块内存就是从data sharing的writer pool中申请的,这样数据直接就是保存在这里的,payload pool就是从这里申请的,申请的是data sharing的writer pool,而writer pool就是基于共享内存创建的。所以说,在发送时,没有拷贝。

std::shared_ptr<IPayloadPool> DataWriterImpl::get_payload_pool()

{

    if (!payload_pool_)

    {

        // Avoid calling the serialization size functors on PREALLOCATED mode

        fixed_payload_size_ =

                pool_config_.memory_policy == PREALLOCATED_MEMORY_MODE ? pool_config_.payload_initial_size : 0u;

 

        // Get payload pool reference and allocate space for our history

        if (is_data_sharing_compatible_)

        {

            payload_pool_ = DataSharingPayloadPool::get_writer_pool(pool_config_);

        }

        else

        {

            payload_pool_ = TopicPayloadPoolRegistry::get(topic_->get_name(), pool_config_);

            if (!std::static_pointer_cast<ITopicPayloadPool>(payload_pool_)->reserve_history(pool_config_, false))

            {

                payload_pool_.reset();

            }

        }

 

        // Prepare loans collection for plain types only

        if (type_->is_plain(data_representation_))

        {

            loans_.reset(new LoanCollection(pool_config_));

        }

    }

 

    return payload_pool_;

}

在这个函数里把数据发送到对方

DeliveryRetCode StatefulWriter::deliver_sample_nts(

        CacheChange_t* cache_change,

        RTPSMessageGroup& group,

        LocatorSelectorSender& locator_selector, // Object locked by FlowControllerImpl

        const std::chrono::time_point<std::chrono::steady_clock>& max_blocking_time)

{

    DeliveryRetCode ret_code = DeliveryRetCode::DELIVERED;

 

    if (there_are_local_readers_)

    {

        deliver_sample_to_intraprocesses(cache_change);

    }

 

    // Process datasharing then

    if (there_are_datasharing_readers_)

    {

        deliver_sample_to_datasharing(cache_change);

    }

 

    if (there_are_remote_readers_)

    {

        ret_code = deliver_sample_to_network(cache_change, group, locator_selector, max_blocking_time);

    }

 

    check_acked_status();

 

    return ret_code;

}

 发送侧通知接收侧

(gdb) bt
#0  eprosima::fastdds::rtps::DataSharingNotifier::notify (this=0x7fffe00029b0) at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/DataSharing/DataSharingNotifier.hpp:78
#1  0x00007ffff7788956 in eprosima::fastdds::rtps::ReaderLocator::datasharing_notify (this=0x7fffe0002568) at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/writer/ReaderLocator.cpp:237
#2  0x00007ffff77a3cf8 in eprosima::fastdds::rtps::ReaderProxy::datasharing_notify (this=0x7fffe0002560) at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/writer/ReaderProxy.hpp:388
#3  0x00007ffff779761a in eprosima::fastdds::rtps::StatefulWriter::deliver_sample_to_datasharing (this=0x555555789150, change=0x555555786f70)
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/writer/StatefulWriter.cpp:612
#4  0x00007ffff779eab1 in eprosima::fastdds::rtps::StatefulWriter::deliver_sample_nts (this=0x555555789150, cache_change=0x555555786f70, group=..., locator_selector=..., max_blocking_time=...)
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/writer/StatefulWriter.cpp:2130
#5  0x00007ffff762d4af in eprosima::fastdds::rtps::FlowControllerImpl<eprosima::fastdds::rtps::FlowControllerSyncPublishMode, eprosima::fastdds::rtps::FlowControllerFifoSchedule>::add_new_sample_impl<eprosima::fastdds::rtps::FlowControllerSyncPublishMode> (this=0x55555567f9d0, writer=0x555555789150, change=0x555555786f70, max_blocking_time=...)
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/flowcontrol/FlowControllerImpl.hpp:1191
#6  0x00007ffff762a44c in eprosima::fastdds::rtps::FlowControllerImpl<eprosima::fastdds::rtps::FlowControllerSyncPublishMode, eprosima::fastdds::rtps::FlowControllerFifoSchedule>::add_new_sample (
    this=0x55555567f9d0, writer=0x555555789150, change=0x555555786f70, max_blocking_time=...) at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/flowcontrol/FlowControllerImpl.hpp:1012
#7  0x00007ffff7796415 in eprosima::fastdds::rtps::StatefulWriter::unsent_change_added_to_history (this=0x555555789150, change=0x555555786f70, max_blocking_time=...)
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/writer/StatefulWriter.cpp:386
#8  0x00007ffff7644784 in eprosima::fastdds::rtps::WriterHistory::notify_writer (this=0x555555788ca0, a_change=0x555555786f70, max_blocking_time=...)
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/history/WriterHistory.cpp:201
#9  0x00007ffff71efd05 in eprosima::fastdds::rtps::WriterHistory::add_change_with_commit_hook<eprosima::fastdds::dds::DataWriterImpl::perform_create_new_change(eprosima::fastdds::rtps::ChangeKind_t, void const*, eprosima::fastdds::rtps::WriteParams&, const InstanceHandle_t&)::<lambda(eprosima::fastdds::dds::DataWriterImpl::CacheChange_t&)> >(eprosima::fastdds::rtps::CacheChange_t *, eprosima::fastdds::rtps::WriteParams &, struct {...}, std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1, 1000000000> > >) (this=0x555555788ca0, a_change=0x555555786f70,
    wparams=..., pre_commit=..., max_blocking_time=...) at /root/Fast-DDS/Fast-DDS/include/fastdds/rtps/history/WriterHistory.hpp:299
#10 0x00007ffff71ef6a0 in eprosima::fastdds::dds::DataWriterHistory::add_pub_change_with_commit_hook<eprosima::fastdds::dds::DataWriterImpl::perform_create_new_change(eprosima::fastdds::rtps::ChangeKind_t, void const*, eprosima::fastdds::rtps::WriteParams&, const InstanceHandle_t&)::<lambda(eprosima::fastdds::dds::DataWriterImpl::CacheChange_t&)> >(eprosima::fastdds::rtps::CacheChange_t *, eprosima::fastdds::rtps::WriteParams &, struct {...}, std::unique_lock<std::recursive_timed_mutex> &, const std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1, 1000000000> > > &) (this=0x555555788ca0, change=0x555555786f70, wparams=..., pre_commit=..., lock=..., max_blocking_time=...) at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/DataWriterHistory.hpp:159
#11 0x00007ffff71e830d in eprosima::fastdds::dds::DataWriterImpl::perform_create_new_change (this=0x55555577d430, change_kind=eprosima::fastdds::rtps::ALIVE, data=0x7ffff6233ef4, wparams=..., handle=...)
    at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/DataWriterImpl.cpp:1057
#12 0x00007ffff71e88e0 in eprosima::fastdds::dds::DataWriterImpl::create_new_change_with_params (this=0x55555577d430, changeKind=eprosima::fastdds::rtps::ALIVE, data=0x7ffff6233ef4, wparams=...)
    at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/DataWriterImpl.cpp:1131
#13 0x00007ffff71e7ba9 in eprosima::fastdds::dds::DataWriterImpl::create_new_change (this=0x55555577d430, changeKind=eprosima::fastdds::rtps::ALIVE, data=0x7ffff6233ef4)
    at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/DataWriterImpl.cpp:976
#14 0x00007ffff71e6312 in eprosima::fastdds::dds::DataWriterImpl::write (this=0x55555577d430, data=0x7ffff6233ef4) at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/DataWriterImpl.cpp:655
#15 0x00007ffff71d9d8f in eprosima::fastdds::dds::DataWriter::write (this=0x55555577db90, data=0x7ffff6233ef4) at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/DataWriter.cpp:84
#16 0x0000555555601386 in eprosima::fastdds::examples::delivery_mechanisms::PublisherApp::publish (this=0x555555657c60) at /root/Fast-DDS/Fast-DDS/examples/cpp/delivery_mechanisms/PublisherApp.cpp:295
#17 0x00005555556010b3 in eprosima::fastdds::examples::delivery_mechanisms::PublisherApp::run (this=0x555555657c60) at /root/Fast-DDS/Fast-DDS/examples/cpp/delivery_mechanisms/PublisherApp.cpp:266
#18 0x0000555555609105 in std::__invoke_impl<void, void (eprosima::fastdds::examples::delivery_mechanisms::Application::*)(), std::shared_ptr<eprosima::fastdds::examples::delivery_mechanisms::Application>>(std::__invoke_memfun_deref, void (eprosima::fastdds::examples::delivery_mechanisms::Application::*&&)(), std::shared_ptr<eprosima::fastdds::examples::delivery_mechanisms::Application>&&) (
    __f=@0x55555565e578: &virtual table offset 16, __t=...) at /usr/include/c++/11/bits/invoke.h:74
#19 0x000055555560903d in std::__invoke<void (eprosima::fastdds::examples::delivery_mechanisms::Application::*)(), std::shared_ptr<eprosima::fastdds::examples::delivery_mechanisms::Application> > (
    __fn=@0x55555565e578: &virtual table offset 16) at /usr/include/c++/11/bits/invoke.h:96
#20 0x0000555555608f9d in std::thread::_Invoker<std::tuple<void (eprosima::fastdds::examples::delivery_mechanisms::Application::*)(), std::shared_ptr<eprosima::fastdds::examples::delivery_mechanisms::Application> > >::_M_invoke<0ul, 1ul> (this=0x55555565e568) at /usr/include/c++/11/bits/std_thread.h:259
#21 0x0000555555608f52 in std::thread::_Invoker<std::tuple<void (eprosima::fastdds::examples::delivery_mechanisms::Application::*)(), std::shared_ptr<eprosima::fastdds::examples::delivery_mechanisms::Application> > >::operator() (this=0x55555565e568) at /usr/include/c++/11/bits/std_thread.h:266
#22 0x0000555555608f32 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (eprosima::fastdds::examples::delivery_mechanisms::Application::*)(), std::shared_ptr<eprosima::fastdds::examples::delivery_mechanisms::Application> > > >::_M_run (this=0x55555565e560) at /usr/include/c++/11/bits/std_thread.h:211
#23 0x00007ffff60dc253 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#24 0x00007ffff5c94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#25 0x00007ffff5d26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb)
 

 

DATA-SHARING初始化writer pool

(gdb) bt
#0  eprosima::fastdds::rtps::WriterPool::init_shared_segment<eprosima::fastdds::rtps::SharedSegment<boost::interprocess::basic_managed_shared_memory<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, unsigned int, unsigned long, 0ul>, 0ul>, boost::interprocess::iset_index>, boost::interprocess::shared_memory_object> > (this=0x5555556633c0,
    writer=0x555555789150, shared_dir="") at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/DataSharing/./WriterPool.hpp:132
#1  0x00007ffff760f455 in eprosima::fastdds::rtps::WriterPool::init_shared_memory (this=0x5555556633c0, writer=0x555555789150, shared_dir="")
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/DataSharing/./WriterPool.hpp:248
#2  0x00007ffff777f315 in eprosima::fastdds::rtps::BaseWriter::init (this=0x555555789150, att=...) at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/writer/BaseWriter.cpp:388
#3  0x00007ffff777e14f in eprosima::fastdds::rtps::BaseWriter::BaseWriter (this=0x555555789150, impl=0x555555663600, guid=..., att=..., flow_controller=0x55555567f9d0, hist=0x555555788ca0,
    listen=0x55555577d930) at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/writer/BaseWriter.cpp:82
#4  0x00007ffff7794db9 in eprosima::fastdds::rtps::StatefulWriter::StatefulWriter (this=0x555555789150, pimpl=0x555555663600, guid=..., att=..., flow_controller=0x55555567f9d0, history=0x555555788ca0,
    listener=0x55555577d930) at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/writer/StatefulWriter.cpp:203
#5  0x00007ffff7685df0 in operator() (__closure=0x7fffffffbc50, guid=..., watt=..., flow_controller=0x55555567f9d0, persistence=0x0, is_reliable=true)
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/participant/RTPSParticipantImpl.cpp:1246
#6  0x00007ffff768f132 in eprosima::fastdds::rtps::RTPSParticipantImpl::create_writer<eprosima::fastdds::rtps::RTPSParticipantImpl::create_writer(eprosima::fastdds::rtps::RTPSWriter**, eprosima::fastdds::rtps::WriterAttributes&, eprosima::fastdds::rtps::WriterHistory*, eprosima::fastdds::rtps::WriterListener*, const eprosima::fastdds::rtps::EntityId_t&, bool)::<lambda(const GUID_t&, eprosima::fastdds::rtps::WriterAttributes&, eprosima::fastdds::rtps::FlowController*, eprosima::fastdds::rtps::IPersistenceService*, bool)> >(eprosima::fastdds::rtps::RTPSWriter **, eprosima::fastdds::rtps::WriterAttributes &, const eprosima::fastdds::rtps::EntityId_t &, bool, const struct {...} &) (this=0x555555663600, writer_out=0x7fffffffbe88, param=..., entity_id=..., is_builtin=false, callback=...)
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/participant/RTPSParticipantImpl.cpp:955
#7  0x00007ffff7686266 in eprosima::fastdds::rtps::RTPSParticipantImpl::create_writer (this=0x555555663600, WriterOut=0x7fffffffbe88, watt=..., hist=0x555555788ca0, listen=0x55555577d930, entityId=...,
    isBuiltin=false) at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/participant/RTPSParticipantImpl.cpp:1264
#8  0x00007ffff76d5f74 in eprosima::fastdds::rtps::RTPSDomainImpl::create_rtps_writer (p=0x5555556631f0, entity_id=..., watt=..., hist=0x555555788ca0, listen=0x55555577d930)
    at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/RTPSDomain.cpp:384
#9  0x00007ffff71e4834 in eprosima::fastdds::dds::DataWriterImpl::enable (this=0x55555577d430) at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/DataWriterImpl.cpp:375
#10 0x00007ffff783570a in eprosima::fastdds::statistics::dds::DataWriterImpl::enable (this=0x55555577d430) at /root/Fast-DDS/Fast-DDS/src/cpp/statistics/fastdds/publisher/DataWriterImpl.hpp:77
#11 0x00007ffff71d9ceb in eprosima::fastdds::dds::DataWriter::enable (this=0x55555577db90) at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/DataWriter.cpp:63
#12 0x00007ffff7204a76 in eprosima::fastdds::dds::PublisherImpl::create_datawriter (this=0x55555577c8a0, topic=0x555555668dd0, impl=0x55555577d430, mask=...)
    at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/PublisherImpl.cpp:277
#13 0x00007ffff720480c in eprosima::fastdds::dds::PublisherImpl::create_datawriter (this=0x55555577c8a0, topic=0x555555668dd0, qos=..., listener=0x555555657c68, mask=...,
    payload_pool=std::shared_ptr<eprosima::fastdds::rtps::IPayloadPool> (empty) = {...}) at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/PublisherImpl.cpp:257
#14 0x00007ffff7202b0c in eprosima::fastdds::dds::Publisher::create_datawriter (this=0x55555577ced0, topic=0x555555668dd0, qos=..., listener=0x555555657c68, mask=...,
    payload_pool=std::shared_ptr<eprosima::fastdds::rtps::IPayloadPool> (empty) = {...}) at /root/Fast-DDS/Fast-DDS/src/cpp/fastdds/publisher/Publisher.cpp:119
#15 0x0000555555600ae1 in eprosima::fastdds::examples::delivery_mechanisms::PublisherApp::PublisherApp (this=0x555555657c60, config=..., topic_name="delivery_mechanisms_topic")
    at /root/Fast-DDS/Fast-DDS/examples/cpp/delivery_mechanisms/PublisherApp.cpp:221
#16 0x00005555555daf5f in __gnu_cxx::new_allocator<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp>::construct<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp, eprosima::fastdds::examples::delivery_mechanisms::CLIParser::delivery_mechanisms_config const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (this=0x7fffffffdddf,
    __p=0x555555657c60) at /usr/include/c++/11/ext/new_allocator.h:162
#17 0x00005555555daafc in std::allocator_traits<std::allocator<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp> >::construct<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp, eprosima::fastdds::examples::delivery_mechanisms::CLIParser::delivery_mechanisms_config const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (__a=...,
    __p=0x555555657c60) at /usr/include/c++/11/bits/alloc_traits.h:516
#18 0x00005555555da3d7 in std::_Sp_counted_ptr_inplace<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp, std::allocator<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp>, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace<eprosima::fastdds::examples::delivery_mechanisms::CLIParser::delivery_mechanisms_config const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (this=0x555555657c50, __a=...) at /usr/include/c++/11/bits/shared_ptr_base.h:519
#19 0x00005555555d9d73 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp, std::allocator<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp>, eprosima::fastdds::examples::delivery_mechanisms::CLIParser::delivery_mechanisms_config const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (this=0x7fffffffdf88, __p=@0x7fffffffdf80: 0x0, __a=...) at /usr/include/c++/11/bits/shared_ptr_base.h:650
#20 0x00005555555d9ade in std::__shared_ptr<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp>, eprosima::fastdds::examples::delivery_mechanisms::CLIParser::delivery_mechanisms_config const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (this=0x7fffffffdf80, __tag=...) at /usr/include/c++/11/bits/shared_ptr_base.h:1342
#21 0x00005555555d97e1 in std::shared_ptr<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp>::shared_ptr<std::allocator<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp>, eprosima::fastdds::examples::delivery_mechanisms::CLIParser::delivery_mechanisms_config const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (this=0x7fffffffdf80,
    __tag=...) at /usr/include/c++/11/bits/shared_ptr.h:409
#22 0x00005555555d948f in std::allocate_shared<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp, std::allocator<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp>, eprosima::fastdds::examples::delivery_mechanisms::CLIParser::delivery_mechanisms_config const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (__a=...)
    at /usr/include/c++/11/bits/shared_ptr.h:863
#23 0x00005555555d916d in std::make_shared<eprosima::fastdds::examples::delivery_mechanisms::PublisherApp, eprosima::fastdds::examples::delivery_mechanisms::CLIParser::delivery_mechanisms_config const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> () at /usr/include/c++/11/bits/shared_ptr.h:879
#24 0x00005555555d885a in eprosima::fastdds::examples::delivery_mechanisms::Application::make_app (config=..., topic_name="delivery_mechanisms_topic")
    at /root/Fast-DDS/Fast-DDS/examples/cpp/delivery_mechanisms/Application.cpp:41
--Type <RET> for more, q to quit, c to continue without paging--
#25 0x0000555555603cb9 in main (argc=4, argv=0x7fffffffe398) at /root/Fast-DDS/Fast-DDS/examples/cpp/delivery_mechanisms/main.cpp:54
(gdb) p shared_dir
$1 = ""
(gdb) s
136             segment_id_ = writer->getGuid();
(gdb) n
137             segment_name_ = generate_segment_name(shared_dir, segment_id_);
(gdb) n
138             std::unique_ptr<T> local_segment;
(gdb) p segment_id_
$2 = {guidPrefix = {static size = 12, value = "\001\017\325\227\065\253k[\000\000\000"}, entityId = {static size = 4, value = "\000\000\001\003"}}
(gdb) p segment_name_
$3 = "fast_datasharing_01.0f.d5.97.35.ab.6b.5b.00.00.00.00_0.0.1.3"
(gdb)
 

2.2接收

DataSharingListener::run

 

3相同点:均通过共享内存进行通信

3.1SHM

在linux下,共享内存默认使用/dev/shm临时文件系统。如下是使用SHM的时候,启动了一个publisher和subscriber,在/dev/shm中创建的文件。

(1)SHM使用了端口的概念

fastdds中对SHM的使用,借鉴了UDP的通信方式,也使用了端口这个概念。在通信中,端口的表示这个通信通道被哪个进程使用。

上图中7400、7410、7411、7412、7413端口与下文中UDP的端口,作用是相同的。

fastdds:传输层端口号计算规则_fastdds mechanism-CSDN博客

 

下图中显示了SHM传输层的通信时序图。可以看到port是用来传输descriptor的,真正的数据是通过fastdds_56f311faa23f4389这样的文件进行传输的。descriptor在网络通信中是经常见到的一个概念,作为数据的描述符描述了数据的基本信息,比如数据的起始地址,数据的长度等。在网卡驱动中,经常说的BD,全称是buffer descriptor,也是网络收发包的描述符,与这里的descriptor是类似的。

(2)文件的作用

①锁,进程间同步

上图中以sem开头,以mutex结尾的文件用于进程间同步。以sem.xxx.port7413_mutex为例,如果一个进程监听7413这个端口的数据,那么就会wait这个锁;如果有进程要向7413发数据,那么写好数据之后,便会notiy这个锁,从而接收方就会被唤醒,进而接收并处理数据。

②fastdds_port7413

这个是端口文件,用于传输descriptor,如果有进程要向7413发送数据,那么便会将descriptor写到这个文件中。

③fastdds_56f311faa23f4389

数据文件,进程要发送的数据都会写到这个文件中。

也就是说,通过共享内存发送数据的时候,首先要将数据写到这个文件中,然后再填充一个desctiptor,将descriptor文件放到port文件中,最后调用notify,环形对应port的进程。

当我们想要了解这些文件是什么用的时候,除了从文件的名字、代码、文档中去查找确认之外,还可以通过在gdb中对shm_open设置断点,从而查看调用栈的方式去学习。/dev/shm中的文件通过shm_open打开。

 

3.2DATA-SHARING

如下是使用DATA-SHARING,启动了一个publisher和subscriber的情况下,在/dev/shm下创建的文件。从下图中可以看出,使用DATA-SHARING 的时候,/dev/shm下的文件,除了使用SHM时的哪些文件之外,还多了一个文件,比如fast_datasharing_01.0f.d5.97.90.46.db.3a.00.00.00.00_0.0.1.3。

 

那么文件fast_datasharing_01.0f.d5.97.90.46.db.3a.00.00.00.00_0.0.1.3是做什么用的呢?

首先我们可以在gdb中对shm_open设断点,查看这个函数的调用栈。调用栈如下图所示:

从中我们可以看到这个函数,可以大胆猜测在使用DATA-SHARING的时候,这个文件用于数据的传输。而原来与SHM传输层相同的文件,用于服务发现阶段。

#8  0x00007ffff7610de5 in eprosima::fastdds::rtps::WriterPool::init_shared_segment<eprosima::fastdds::rtps::SharedSegment<boost::interprocess::basic_managed_shared_memory<char, boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, boost::interprocess::offset_ptr<void, unsigned int, unsigned long, 0ul>, 0ul>, boost::interprocess::iset_index>, boost::interprocess::shared_memory_object> > (
    this=0x5555556633c0, writer=0x555555789150, shared_dir="") at /root/Fast-DDS/Fast-DDS/src/cpp/rtps/DataSharing/./WriterPool.hpp:183

4不同点

(1)SHM是标准的传输层port based的传输层,DATA-SHARING不是

(2)相对于SHM,DATA-SHARING在发送侧少一次拷贝

(3)SHM数据和Descriptor保存在了不同的文件中,数据保存在fastdds_683379ac5b6c3ba5中,descriptor发送到fastdds_port7413中;而DATA-SHARING的数据和描述符保存在了同一块内存中,下边的函数,node就相当于SHM中的descriptor,可以看到node和数据所在的内存是爱着的。

    void add_to_shared_history(

            const CacheChange_t* cache_change)

    {

        assert(cache_change);

        assert(cache_change->serializedPayload.data);

        assert(cache_change->serializedPayload.payload_owner == this);

        assert(free_history_size_ > 0);

 

        // Fill the payload metadata with the change info

        PayloadNode* node = PayloadNode::get_from_data(cache_change->serializedPayload.data);

        node->status(ALIVE);

        node->data_length(cache_change->serializedPayload.length);

        node->source_timestamp(cache_change->sourceTimestamp);

        node->writer_GUID(cache_change->writerGUID);

        node->instance_handle(cache_change->instanceHandle);

        if (cache_change->write_params.related_sample_identity() != SampleIdentity::unknown())

        {

            node->related_sample_identity(cache_change->write_params.related_sample_identity());

        }

 

        // Set the sequence number last, it signals the data is ready

        node->sequence_number(cache_change->sequenceNumber);

 

        // Add it to the history

        history_[static_cast<uint32_t>(descriptor_->notified_end)] = segment_->get_offset_from_address(node);

        EPROSIMA_LOG_INFO(DATASHARING_PAYLOADPOOL, "Change added to shared history"

                << " with SN " << cache_change->sequenceNumber);

        advance(descriptor_->notified_end);

        --

(4)SHM发送侧和接收侧的notify和wait是以sem.fastdds_portxxxx_mutex为桥梁;DATA-SHARING的notify和wait就是以数据传输文件为桥梁。

 

相关文章:

fastdds:传输层SHM和DATA-SHARING的区别

下图是fastdds官方的图&#xff0c;清晰地展示了dds支持的传输层: 根据通信双方的相对位置(跨机器、同机器跨进程、同进程)的不同选择合适的传输层&#xff0c;是通信中间件必须要考虑的事情。 跨机器&#xff1a;udp、tcp 跨机器通信&#xff0c;只能通过网络&#xff0c; f…...

MQ基础篇

1.初识MQ 1.同步调用 概念&#xff1a; 同步调用是一种程序执行方式&#xff0c;在调用一个函数或服务时&#xff0c;调用方会一直等待被调用方执行完成并返回结果&#xff0c;才会继续执行后续代码 &#xff0c;期间调用线程处于阻塞状态。 同步调用的优势&#xff1a; 时…...

网络编程2

day2 一、UDP编程 1.编程流程 2.函数接口 3.注意 (1)、对于TCP是先运行服务器&#xff0c;客户端才能运行。(2)、对于UDP来说&#xff0c;服务器和客户端运行顺序没有先后&#xff0c;因为是无连接&#xff0c;所以服务器和客户端谁先开始&#xff0c;没有关系.(3)、一个服务器…...

Python环境中在线训练机器学习模型所遇到的问题及解决方案

我最近开发个智能控制系统,包括实时数据采集、预测、策略优化等功能,最近增加在线学习功能,也就是在线进行模型训练,在线进行模型训练时出现了问题,现象为: 控制台报: cmdstanpy - INFO - Chain [1] start processing所有任务、线程停止,Web服务登录无法访问后台的pyt…...

「仓颉编程语言」Demo

仓颉编程语言」Demo python 1)# 仓颉语言写字楼管理系统示例&#xff08;虚构语法&#xff09;# 语法规则&#xff1a;中文关键词 类Python逻辑定义 写字楼管理系统属性:租户库 列表.新建()报修队列 列表.新建()费用单价 5 # 元/平方米方法 添加租户(名称, 楼层, 面积):…...

《软件设计师》复习笔记(11.4)——处理流程设计、系统设计、人机界面设计

目录 一、业务流程建模 二、流程设计工具 三、业务流程重组&#xff08;BPR&#xff09; 四、业务流程管理&#xff08;BPM&#xff09; 真题示例&#xff1a; 五、系统设计 1. 主要目的 2. 设计方法 3. 主要内容 4. 设计原则 真题示例&#xff1a; 六、人机界面设…...

win11系统截图的几种方式

在 Windows 11 中&#xff0c;系统内置的截图功能已全面升级&#xff0c;不仅支持多种截图模式&#xff0c;还整合了录屏、OCR 文字识别和 AI 增强编辑等功能。以下是从基础操作到高阶技巧的完整指南&#xff1a; 一、快捷键截图&#xff08;效率首选&#xff09; 1. Win Sh…...

http://noi.openjudge.cn/——2.5基本算法之搜索——1998:寻找Nemo

文章目录 题目宽搜代码优先队列深搜代码小结 题目 总时间限制: 2000ms 内存限制: 65536kB 描述 Nemo 是个顽皮的小孩. 一天他一个人跑到深海里去玩. 可是他迷路了. 于是他向父亲 Marlin 发送了求救信号.通过查找地图 Marlin 发现那片海像一个有着墙和门的迷宫.所有的墙都是平行…...

win10系统完美配置mamba-ssm全整合方案

好久没瞎写东西了&#xff0c;刚好最近遇到一个逆天需求&#xff1a;要在win10平台上配置可用的mamba-ssm环境。由于这个环境原版以及相关依赖都是仅适配linux的&#xff0c;即使是依赖conda环境直接拿来往windows系统上装也全是bug&#xff0c;网上大量的垃圾教程也都是错的&a…...

MQTTClient.c中的协议解析与报文处理机制

MQTTClient.c中的协议解析与报文处理机制 1. 协议解析的核心逻辑 &#xff08;1&#xff09;报文头部解析 MQTT协议报文由固定头&#xff08;Fixed Header&#xff09; 可变头&#xff08;Variable Header&#xff09; 负载&#xff08;Payload&#xff09;三部分组成。在rea…...

LeetCode每日一题4.18

2364.统计坏数对的数目 问题 问题分析 根据题目要求&#xff0c;(i, j) 是一个坏数对的条件是&#xff1a; i < j j - i ! nums[j] - nums[i]&#xff0c;即 nums[j] - j ! nums[i] - i 因此&#xff0c;我们可以转换问题&#xff1a;对于每个 j&#xff0c;找到所有 i &l…...

cmd查询占用端口并查杀

查看特定端口的占用情况 netstat -ano | findstr 端口号 netstat -ano | findstr 端口号 结束指定进程 askkill /T /F /PID PID askkill /T /F /PID PID...

ETL数据集成平台在交通运输行业的五大应用场景

在智能交通与数字物流时代&#xff0c;交通运输企业每天产生海量数据——车辆轨迹、货物状态、乘客流量、设备日志……但这些数据往往被困在分散的系统中&#xff1a;GPS定位数据躺在车载终端里&#xff0c;物流订单卡在Excel表中&#xff0c;地铁客流统计锁在本地服务器内。如…...

自定义 el-menu

使用的工具&#xff1a;vue2 element-ui <!DOCTYPE html> <html><head><link rel"stylesheet" href"https://unpkg.com/element-ui/lib/theme-chalk/index.css"><style>.el-menu--horizontal {border-bottom: none !impor…...

创维E900V20C-国科GK6323V100C-rtl8822cs-安卓9.0-短接强刷卡刷固件包

创维E900V20C&#xff0f;创维E900V20D-国科GK6323V100C-安卓9.0-强刷卡刷固件包 创维E900V20C 刷机说明&#xff1a; 1、用个老款4G&#xff0c;2.0的U盘&#xff0c;fat32&#xff0c;2048块单分区格式化&#xff0c; 5个文件复制到根目录&#xff0c;插盒子靠网口U口&…...

DemoGen:用于数据高效视觉运动策略学习的合成演示生成

25年2月来自清华、上海姚期智研究院和上海AI实验室的论文“DemoGen: Synthetic Demonstration Generation for Data-Efficient Visuomotor Policy Learning”。 视觉运动策略在机器人操控中展现出巨大潜力&#xff0c;但通常需要大量人工采集的数据才能有效执行。驱动高数据需…...

影楼精修-高低频磨皮算法解析

注意&#xff1a;本文样例图片为了避免侵权&#xff0c;均使用AIGC生成&#xff1b; 高低频磨皮基础 高低频磨皮是一种常用于人像后期修图的技术&#xff0c;它能在保留皮肤纹理的同时柔化瑕疵&#xff0c;使皮肤看起来更加自然细腻。高低频磨皮的算法原理如下&#xff1a; …...

打造搜索神功:Express 路由中的关键词探查之道

前言 在 Web 开发的江湖,Express 好比一位身怀绝技的武林高手,出手稳准狠,擅长解决各种疑难杂症。今天,我们将与这位高手并肩作战,一探关键词搜索路由的奥义。这不是枯燥的教学,而是一场充满玄机与笑点的江湖奇遇。挥起代码之剑,踏上探索之路,不仅能习得招式,还能在轻…...

kubernetes-使用ceph-csi

kubernetes-使用ceph-csi Kubernetes &#xff08;简称K8s&#xff09;和Ceph都是开源的云计算技术&#xff0c;K8s是一个容器编排平台&#xff0c;而Ceph是一个分布式存储系统。将K8s和Ceph集成在一起可以为应用程序提供高可用性和持久性存储。本文主要介绍如何在使用openEul…...

​​从Shell到域控:内网渗透中定位域控制器的8种核心方法​

在内网渗透中&#xff0c;定位域控制器&#xff08;Domain Controller, DC&#xff09;是攻防对抗的关键环节。本文结合实战经验与工具技术&#xff0c;总结出​​8种从Shell快速发现域控主机的方法​​&#xff0c;涵盖命令探测、网络扫描、日志分析等维度&#xff0c;助你系统…...

FA-YOLO:基于FMDS与AGMF的高效目标检测算法解析

本文《FA-YOLO: Research On Efficient Feature Selection YOLO Improved Algorithm Based On FMDS and AGMF Modules》针对YOLO系列在特征融合与动态调整上的不足,提出两种创新模块:​FMDS(细粒度多尺度动态选择模块)​和AGMF(自适应门控多分支聚焦融合模块)​。论文结构…...

【RK3588 嵌入式图形编程】-SDL2-扫雷游戏-结束和重新开始游戏

结束和重新开始游戏 文章目录 结束和重新开始游戏1、概述2、更新Globals.h3、触发GAME_WON和GAME_LOST事件4、对游戏结束的反应5、重启游戏6、创建新游戏按钮7、完整代码8、总结在本文中,将实现胜负检测并添加重新开始功能以完成游戏循环。 1、概述 在本文中,我们将更新我们…...

OpenAI重返巅峰:o3与o4-mini引领AI推理新时代

引言 2025年4月16日&#xff0c;OpenAI发布了全新的o系列推理模型&#xff1a;o3和o4-mini&#xff0c;这两款模型被官方称为“迎今为止最智能、最强大的大语言模型&#xff08;LLM&#xff09;”。它们不仅在AI推理能力上实现了质的飞跃&#xff0c;更首次具备了全面的工具使…...

《软件设计师》复习笔记(12.3)——质量管理、风险管理

目录 一、质量管理 1. 质量定义 2. 质量管理过程 3. 软件质量特性&#xff08;GB/T 16260-2002&#xff09; 4. 补充知识 McCall质量模型&#xff1a; 软件评审 软件容错技术 真题示例&#xff1a; 二、风险管理 1. 风险管理的目的&#xff1a; 2. 风险管理流程及内…...

优化自旋锁的实现

在《C11实现一个自旋锁》介绍了分别使用TAS和CAS算法实现自旋锁的方案&#xff0c;以及它们的优缺点。TAS算法虽然实现简单&#xff0c;但是因为每次自旋时都要导致一场内存总线流量风暴&#xff0c;对全局系统影响很大&#xff0c;一般都要对它进行优化&#xff0c;以降低对全…...

项目实战--新闻分类

从antd中拿一个表格 表格 Table - Ant Designhttps://ant-design.antgroup.com/components/table-cn#table-demo-edit-cell使用的是可编辑单元格 实现引入可编辑单元格&#xff1a; import React, { useState, useEffect, useRef, useContext } from react import { Button, …...

人像面部关键点检测

此工作为本人近期做人脸情绪识别&#xff0c;CBAM模块前是否能加人脸关键点检测而做的尝试。由于创新点不是在于检测点的标注&#xff0c;而是CBAM的改进&#xff0c;因此&#xff0c;只是借用了现成库Dilb与cv2进行。 首先&#xff0c;下载人脸关键点预测模型:Index of /file…...

OpenVINO怎么用

目录 OpenVINO 简介 主要组件 安装 OpenVINO 使用 OpenVINO 的基本步骤 OpenVINO 简介 OpenVINO&#xff08;Open Visual Inference and Neural Network Optimization&#xff09;是英特尔推出的一个开源工具包&#xff0c;旨在帮助开发者在英特尔硬件平台上高效部署深度学…...

写论文时降AIGC和降重的一些注意事项

‘ 写一些研究成果&#xff0c;英文不是很好&#xff0c;用有道翻译过来句子很简单&#xff0c;句型很单一。那么你会考虑用ai吗&#xff1f; 如果语句太正式&#xff0c;高级&#xff0c;会被误判成aigc &#xff0c;慎重选择ai润色。 有的话就算没有用ai生成&#xff0c;但…...

SpringBoot学习(properties、yml(主流)、yaml格式配置文件)(读取yml配置文件的3种方式)(详解)

目录 一、SpringBoot配置文件详解。 1.1配置文件简介。 1.2配置文件分类。&#xff08;3种配置文件格式&#xff09; <1>application.properties&#xff08;properties格式&#xff09;。 <2>application.yml&#xff08;yml格式&#xff09;。 <3>applicat…...

STM32单片机C语言

1、stdint.h简介 stdint.h 是从 C99 中引进的一个标准 C 库的文件 路径&#xff1a;D:\MDK5.34\ARM\ARMCC\include 大家都统一使用一样的标准&#xff0c;这样方便移植 配置MDK支持C99 位操作 如何给寄存器某个值赋值 举个例子&#xff1a;uint32_t temp 0; 宏定义 带参…...

前端为什么需要单元测试?

一. 前言 对于现在的前端工程&#xff0c;一个标准完整的项目&#xff0c;通常情况单元测试是非常必要的。但很多时候我们只是完成了项目而忽略了项目测试。我认为其中一个很大的原因是很多人对单元测试认知不够&#xff0c;因此我写了这篇文章&#xff0c;一方面期望通过这篇…...

QT 文件和文件夹操作

文件操作 1. 文件读写 QFile - 基本文件操作 // 只写模式创建文件&#xff08;如果文件已存在会清空内容&#xff09; file.open(QIODevice::WriteOnly);// 读写模式创建文件 file.open(QIODevice::ReadWrite);// 追加模式&#xff08;如果文件不存在则创建&#xff09; fil…...

AIP目录

专注于开发灵活API的设计文档。 AIP是总结了谷歌API设计决策的设计文档&#xff0c;它也为其他人提供了用文档记录API设计规则和实践的框架和系统。 基础1AIP目的和指南2AIP编号规则3AIP版本管理200先例8AIP风格与指导9术语表流程100API设计评审常见问题205Beta版本发布前置条…...

Function Calling的时序图(含示例)

&#x1f9cd; 用户&#xff1a; 发起请求&#xff0c;输入 prompt&#xff08;比如&#xff1a;“请告诉我北京的天气”&#xff09;。 &#x1f7ea; 应用&#xff1a; 将用户输入的 prompt 和函数定义&#xff08;包括函数名、参数结构等&#xff09;一起发给 OpenAI。 …...

基于尚硅谷FreeRTOS视频笔记——6—滴答时钟—上下文切换

FreeRTOS滴答 FreeRTOS需要有一个时钟参照&#xff0c;并且这个时钟不会被轻易打断&#xff0c;所以最好选择systick 为什么需要时间参照 就是在高优先级任务进入阻塞态后&#xff0c;也可以理解为进入delay&#xff08;&#xff09;函数后&#xff0c;需要有一个时间参照&…...

Playwright框架入门

Playwright爬虫框架入门 Playwright介绍 playwright官方文档 Playwright是一个用于自动化浏览器操作的开源工具&#xff0c;由Microsoft开发和维护&#xff0c;支持多种浏览器和多种编程语言&#xff0c;可以用于测试、爬虫、自动化任务等场景。 Playwright是基于WebSocket…...

针对渲染圆柱体出现“麻花“状问题解决

圆柱体渲染结果&#xff0c;在侧面有麻花状条纹&#xff0c;边缘不够硬朗&#xff0c;上下的圆看起来不够平&#xff0c;很明显&#xff0c;是法向量导致的。 原始模型 渲染结果 计算点的法向量采用简单的平均法&#xf…...

手撕数据结构算法OJ——栈和队列

文章目录 一、前言二、手撕OJ2.1有效的括号2.2用队列实现栈2.2.1初始化2.2.2入栈2.2.3出栈2.2.4取栈顶2.2.5判空2.2.6销毁2.2.7整体代码 2.3用栈实现队列2.3.1初始化2.3.2入队2.3.3出队2.3.4取队头2.3.5判空2.3.6销毁2.3.7整体代码 四、总结 一、前言 兄弟们&#xff0c;今天的…...

基础知识-指针

1、指针的基本概念 1.1 什么是指针 1.1.1 指针的定义 指针是一种特殊的变量&#xff0c;与普通变量存储具体数据不同&#xff0c;它存储的是内存地址。在计算机程序运行时&#xff0c;数据都被存放在内存中&#xff0c;而指针就像是指向这些数据存放位置的 “路标”。通过指针…...

Thymeleaf简介

在Java中&#xff0c;模板引擎可以帮助生成文本输出。常见的模板引擎包括FreeMarker、Velocity和Thymeleaf等 Thymeleaf是一个适用于Web和独立环境的现代服务器端Java模板引擎。 Thymeleaf 和 JSP比较&#xff1a; Thymeleaf目前所作的工作和JSP有相似之处&#xff0c;Thyme…...

ifconfig -bash: ifconfig: command not found

Ubuntu系统安装完成想查看其ip 报错ifconfig -bash: ifconfig: command not found 解决方法 sudo apt update sudo apt install net-tools ip查找成功...

MCP协议量子加密实践:基于QKD的下一代安全通信(2025深度解析版)

一、量子计算威胁的范式转移与MCP协议改造必要性 1.1 传统加密体系的崩塌时间表 根据IBM 2025年量子威胁评估报告&#xff0c;当量子计算机达到4000个逻辑量子比特时&#xff08;预计2028年实现&#xff09;&#xff0c;现有非对称加密体系将在72小时内被完全破解。工业物联网…...

STM32 基本GPIO控制

目录 GPIO基础知识 ​编辑IO八种工作模式 固件库实现LED点灯 蜂鸣器 按键基础知识 ​编辑继电器 震动传感器 433M无线模块 GPIO基础知识 GPIO(General-Purpose input/output,通用输入/输出接口) 用于感知外部信号&#xff08;输入模式&#xff09;和控制外部设备&…...

【天外之物】叉乘(向量积)的行列式表示方法

叉乘&#xff08;向量积&#xff09;的行列式表示方法如下&#xff1a; 步骤说明&#xff1a; 构造33矩阵&#xff1a; 将三维向量叉乘转换为行列式的形式&#xff0c;需构造一个包含单位向量 i , j , k \mathbf{i}, \mathbf{j}, \mathbf{k} i,j,k 和原向量分量的矩阵&#x…...

北京SMT贴片厂精密制造关键工艺

内容概要 随着电子设备小型化与功能集成化需求日益提升&#xff0c;北京SMT贴片厂在精密制造领域持续突破工艺瓶颈。本文以高密度PCB板贴片全流程为核心&#xff0c;系统梳理从锡膏印刷、元件贴装到回流焊接的关键技术节点&#xff0c;并结合自动化检测与缺陷预防方案&#xf…...

服务器架构:SMP、NUMA、MPP及Docker优化指南

文章目录 引言 一、服务器架构基础1. SMP&#xff08;对称多处理&#xff0c;Symmetric Multiprocessing&#xff09;2. NUMA&#xff08;非统一内存访问&#xff0c;Non-Uniform Memory Access&#xff09;3. MPP&#xff08;大规模并行处理&#xff0c;Massively Parallel Pr…...

Datawhale春训营赛题分析和总结

1.Datawhale春训营任务一 借助这个云平台&#xff0c;支持类似于这个anaconda相关的交互式的操作&#xff0c;第一个任务就是跑通这个baseline&#xff0c;然后注册账号之后送了对应的相关算力&#xff0c;跑通这个之后需要进行打卡&#xff0c;跑通其实是没问题不大的&#x…...

一键模仿图片风格,图生生APP,实现随时随地“生图自由“

一、什么是"图片模仿"功能&#xff1f; "图片模仿"是图生生AI的功能之一&#xff0c;利用先进的AI技术&#xff0c;分析上传的图片风格、色调、构图等元素&#xff0c;快速生成具有相同风格的图片。无论是产品展示、广告海报还是社交媒体配图&#xff0c;…...

C++——C++11常用语法总结

C11标准由国际标准化组织&#xff08;ISO&#xff09;和国际电工委员会&#xff08;IEC&#xff09;旗下的C标准委员会&#xff08;ISO/IEC JTC1/SC22/WG21&#xff09;于2011年8月12日公布&#xff0c;并于2011年9月出版。2012年2月28日的国际标准草案(N3376)是最接近于C11标准…...