Apollo 3.5 Cyber 多進程通訊模塊 - Transport (Shared Memory篇)
ROS為不個的node之間的通訊提供了shared memory和network兩種方法 (是也有一個叫intra, 不過那不能cross-process的。而cyber的Node::Reader和Node::Writer是預設三種都用的,即message會視乎情況選對應的方法去發,也可能用三種方法都發一次,也就是hybrid mode了)
Cyber也提供了差不多的東西,今天就看一看
先上一個全局的圖。建議放大看。
當中主要有ShmDispatcher
, ShmReceiver
, ShmTransmitter
,Segment
,NotifierBase
這幾個。其他的就是實現的細節。當你了解這幾個主要class的互動時,就會基本了解其機制了。
每個Class的功用
Segment
: 負責管理一段Shared Memory, 提供Accquire-Release的接口讓其他class可以在thread-safe的前題下拿到shared memory中的對象. 而因為Segment的Block
(s)是放在shared memory的,就算在同一線程中生成同一channel的多個segment,其內容空間也是一樣的Block
: 一段Segment
中有多個Block
,channel中的數據會存在其中,而Block
也是其他class用來讀寫數據的對象State
: 管理Segment
的內部狀態。因為Segment
應該是在多個process中都是有相同狀態的,所以要特別抽出來放到shared memory中去肯定同步。Receiver
:ShmReceiver
的Base class, 它下邊除了ShmReceiver
,還有RtpsReceiver
,IntraReceiver
等等。主要就是負責`當指定channel有數據時,調用指定的回調,並提供一個開關(是否繼續監聽channel)的功能“ShmReceiver
:Receiver
的shared memory版實現,在channel中的數據會利用shared memory去共享Transmitter
:ShmTransmitter
的Base class, 它下邊也是有shared memory版本,rtps版本,等等。主要就是負責`提供一個接口,容許你發數據到指定channel,並告知對應Notifier有更新。並提供一個開關(是否讓其內部指針指到Notifier的singleton,這沒有甚麼用,反正不發數據就和關了差不多)的功能“ShmTransmitter
的shared memory版實現Dispatcher
: 用來管理所有的
channel的讀數據的部份。當有新數據時就調用Receiver
註冊的回調。其subclass都是Singleton。ShmDispatcher
: 做polling
,不停去看Notifier
有沒有新的info過來,如果有新info來了,就解讀info,在對應Segment
中拿數據及把其deserialize為一個對象.最後回調Receiver
的回調Notifier
:發新數據
跟發有新數據的提示
是兩個不同的概念,Dispatcher
做了前者,而Notifier
做了後者,而Notifier也提供了多種實現方式.Notifier
的所有subclass也是單例MulticastNotifier
: 用了multicast頻道,用socket通訊實現的Notifier
ConditionNotifier
: 用conditional variable在shared memory實現的Notifier
NotifierFactory
: 按全局config,生成MulticastNotifier
或ConditionNotifier
ReadableInfo
:Notifier
在有新數據時所發出的結構體,包含了以下幾個資料- 是那個channel
- 是誰發的
- 第幾個block
ListenHandler
: 基本就是callback, 不過加上了一些額外的資訊,容訊做到一些額外功能。比如只有在某一個特定node發數據時才調用。Signal
,Conenct
,Slot
就一個通用的回調機制
由Transmitter
發新數據到Receiver
處理數據的流程
其實看完上邊應該都了解得差不多了,不過都寫一寫
- 調用
Transmitter
的Transmit
接口去發數據
// cyber/transport/transmitter/transmitter.h
virtual bool Transmit(const MessagePtr& msg);
virtual bool Transmit(const MessagePtr& msg, const MessageInfo& msg_info) = 0;
ShmTransmitter
版的Transmit
實現是先從Segment
中拿到可以寫的Block
,然後寫進去。最後告訴Notifier
,有新message了
template <typename M>
bool ShmTransmitter<M>::Transmit(const MessagePtr& msg,
const MessageInfo& msg_info) {
return Transmit(*msg, msg_info);
}
template <typename M>
bool ShmTransmitter<M>::Transmit(const M& msg, const MessageInfo& msg_info) {
if (!this->enabled_) {
ADEBUG << "not enable.";
return false;
}
WritableBlock wb;
std::size_t msg_size = message::ByteSize(msg);
if (!segment_->AcquireBlockToWrite(msg_size, &wb)) {
AERROR << "acquire block failed.";
return false;
}
ADEBUG << "block index: " << wb.index;
if (!message::SerializeToArray(msg, wb.buf, static_cast<int>(msg_size))) {
AERROR << "serialize to array failed.";
segment_->ReleaseWrittenBlock(wb);
return false;
}
wb.block->set_msg_size(msg_size);
char* msg_info_addr = reinterpret_cast<char