百度 Apollo 8.0 CyberRT 源代碼分析(四)

4 cyber Reader接收數據

cyber收發數據有幾種類型:進程內,進程間,跨主機。進程間的收發基于共享內存,跨主機的收發基于fastrtps網路庫。這里以進程間的場景為例說明。

ShmReceiver基于共享內存實現Reciever接口,它委托ShmDispatcher從共享內存讀取數據。

DataDispatcher/DataVisitor及相關類,實現一寫多讀的生產/消費模式。ShmReceiver讀取的數據,會復制到DataDispatcher的環形緩存,這個緩存由DataVisitor提供。(ReceiverManager負責將DataDispatcher連接到receiver)。

每個Reader實例創建一個DataVisitor實例,并向DataDispatcher注冊它。DataVisitor的環形緩存是在構造函數創建的。

Reader通過指定的回調函數接收消息。Reader通過RoutineFactory創建了一個協程,從DataVisitor讀取消息,然后調用這個回調函數。這樣Reader就收到消息了。

4.1 ConditionNotifier與MulticastNotifier

NotifierBase用于一組數據的生產/消費通知,這里一個數據單元稱作block。也
就是,當寫入一個block完成時,writer會通知一個或一組reader。

  • 虛擬成員函數 Notify()用于通知,Listen()用于得到通知。

作為NotfiBase的派生類,ConditionNotifier和MutlicastNotifier的區別是,前者通知一個消費者,后者通知所有消費者。

ConditionNotifier基于共享內存實現。

  • 成員key_ 是基于”/apollo/cyber/transport/shm/notifier”的hash值,用于創建共享內存
  • 成員managed_shm_指向創建的共享內存,其中存放一組Indicator實例,也就是成員indicator_。Indicator保存block信息,如host_id_、channel_id、block_index_等。

MulticastNotifier基于udp組播實現。
+notifier_fd_是socket fd,使用組播地址239.255.0.100/8888。

4.2 Segment

基于共享內存的通信使用Segment傳輸數據,每個channel一個Segment實例。

  • 成員channel_id_是channel id
  • 成員managed_shm_ 是共享內存塊。成員state_ 和blocks_在managed_shm_分配,分別保存meta信息和block數據。每個block一塊數據。
  • 如果共享內存塊還沒創建,OpenOrCreate()創建它,如果已經創建了,OpenOnly()打開它。
  • 成員函數AcquireBlockToWrite()/ReleaseWrittenBlocks()用于寫block,AcquireBlockToRead()/ReleaseReadBlock()用于讀block。

XsiSegment和PosixSegment的區別是創建共享內存塊managed_shm_ 的方式不同。

SegmentFactory根據配置文件的選項創建XsiSegment/PosixSegment實例。

4.3 Dispatcher / ListenserHandler

ListenerHandlerBase定義了消息通知接口,ListenerHandler基于Signal實現這個接口。

  • 成員signal_ 是Signal實例。成員函數Connect()將指定回調函數連接到signal_ 上,以便得到通知。
  • 成員signal_conns_ 是從某種類型id到Signal實例的映射。
  • 成員函數Run()使用給定的消息,調用signal_中的回調函數。

Dispatcher從傳輸層接收消息,并向上層派發。

  • 成員msg_listeners_ 是從channel_id到ListenserHandleBase(實際上就是ListenerHandler)實例的映射。
  • 成員函數AdListener()/RemoveListner()注冊指定的回調函數。它在msg_listeners_查找MessageListener實例,如果沒有就增加一個新的;將回調函數連接MessageListener的成員signal_上。這樣當MessageListener::Run()放入消息時,回調函數會被調用。

4.4 ShmDispatcher

ShmDispatcher基于共享內存實現Dispatcher接口。

  • 成員host_id_是本地主機id。
  • 成員segments_ 是從channel_id到Segement實例的映射。從segments_獲取消息數據,一個channel有一個Segment。成員函數AddSegment()向segments增加Segment實例。
  • 當某個channel的Segment有消息到達時,可以從成員notifier_得到通知。notifier_負責多個channel。
  • 注意ShmDispatcher有自己版本的AddListener(),使用模板參數Message。其中定義定義了一個將Message適配到ReadableBlock的函數。后面這個函數使用模板參數ReadableBlock調用Dispatcher::AddListener()。
  • 成員thread_是接收消息的線程。它等待成員notifier_ 的通知;根據得到的channel調用ReadMessage()。
  • 在ReadMessage()中,從相應channel的Segment獲取ReadableBlock類型數據,從數據中反序列化,得到MessageInfo信息(沒有數據部分),調用OnMessage()。
  • 在OnMessage()中,從msg_listeners_中找到相應channel_id的ListenerHandler實例,調用ListenerHandler::Run();從msg_listeners_找到對應channel的ListenerHandler實例,然后調用功能ListenerHandler::Run()。這樣之前ShmDispatcher::AddListener()中定義的適配函數會被調用。
  • 這個適配函數從ReadableBlock解析出Message,然后調用使用者的回調函數。

4.5 Receiver

Receiver負責接收消息。

  • 成員函數OnNewMessage()在消息到達被調用,然后它會調用成員msg_listener_。后者由使用者通過Receiver構造函數指定。
  • 虛擬成員函數Enable()開始接收消息,Disable()停止接收消息。派生類一般在這里注冊/反注冊OnNewMessage()。

4.6 ShmReceiver

ShmReceiver基于共享內存實現Receiver接口,它將主要工作委托給ShmDispatcher。

  • 成員dispatcher_ 是ShmDispatcher實例。
  • ShmReceiver定義了自己版本的Enable()/Disable(),以便把Receiver::OnNewMessage()加入到dispatcher_的監聽者隊列中。當dispatcher_收到消息時,OnNewMessage()會調用使用者的回調函數。這個回調函數在ShmReceiver/Receiver的構造函數中指定。

4.7 HybridReceiver

HybridReceiver是混合型Receiver實例,它實際上是將工作委托給自己的成員去處理,這些成員其他基本類型的Receiver,如ShmReceiver。

4.8 Transport

Transport負責根據指定的Mode創建Receiver/Transmiter實例。

4.9 cyber如何使用DataVisitor向多個Reader推送消息

DataVisitor在cyber中的使用場景如下:

  • 在全局函數CreateRoutineFactory()中,創建RoutineFactory的實例。它的成員create_routine是函數指針,成員data_visitor_是DataVisitor實例。如前面介紹DataVisitor時所述,它的成員notifier_是一個回調函數。
  • 用這個RoutineFactory實例作為參數,調用Scheduler::CreateTask()。
    • 創建CRoutine實例,協程處理函數設置為RoutineFactory::create_routine,
    • 定義一個函數,將它設置為RoutineFactory.data_visitor_.notifier_。
  • 當消息到達時,DataDispatcher::dispatch()被調用,消息保存到成員buffer_中;同時成員notifier_被調用,這個回調函數會通知Scheduler更新當前協程的等待狀態,進入可調度狀態;如果協程的處理線程處于等待狀態,還會喚醒它。

  • 回到RoutineFactory::create_routine的實現。這個函數在一個for()循環中,

    • 調用DataVisitor::TryFetch(),嘗試從成員buffer_ 獲取消息。如果有消息,則調用用戶提供的回調函數;如果沒有,則調用CRoutine::Yield(),協程放棄剩余的時間片,進入等待。
  • 對于Reader,它在Reader::Init()中定義了一個回調函數。這樣Reader實例就得到了消息。

4.10 ReceiverManager

ReceieverManager基于Transport創建Receiver實例。這里定義了一個函數綁定到Receiver實例上。當消息到達時,調用DataDispatcher::Dispatch()推送它,保存到DataVisitor實例中。

5 cyber Writer發送數據

cyber收發數據有幾種類型:進程內,進程間,跨主機。進程間的收發基于共享內存,跨主機的收發基于fastrtps網路庫。這里以進程間的場景為例說明。

5.1 Transmitter

Transmitter負責發送消息。

  • 成員函數Transmit()發送消息。
  • 虛擬成員函數Enable()使能發送消息,Disable()停止發送消息。

5.2 ShmTrasmitter

ShmTransmitter基于共享內存實現Transmitter接口。

  • 成員channel_id_ 是channel id
  • 成員segment_ 是Segment實例,數據寫入這里。
  • 成員notifer_是Notifier實例,用于通知對端數據準備好了。

5.3 HybridTransmitter

HybridTransmitter是混合型Transmitter實例,它實際上是將工作委托給自己的成員去處理,這些成員其他基本類型的Transmitter,如ShmTransmitter。

6 cyber 跨主機收發數據的場景

前面以進程間的場景說明了cyber如何收發數據,這里說明跨主機收發數據的場景。

RtpsDispatcher派生自Dispatcher,負責從fastrtps接收指定channel的消息,然后派發給上層的DataDispatcher/DataVisitor實例,進而被Reader接收。

  • 成員participant是Participant實例。
  • 成員 subs_是一個從channel id到Subcriber實例的映射。Subriber的成員包括fastrtps_Subscriber和SubListener實例,用于監聽fastrtps上指定channel。

RtpsTransmitter派生自Transmitter,負責通過fastrtps發送指定channel的消息。消息發送之前,會先序列化成字符串。

  • 成員participant是Participant實例。
  • 成員publisher_ 是fastrtps_Publisher實例。

7 cyber 收發消息的底層消息

進程間通信時,ShmTransmitter/ShmDispatcher傳輸的消息單元是ReadableBlock/WritableBlock;跨主機通信時,RtpsTranmistter/RtpsDispatcher傳輸的消息單元是UnderlayMessage。

MessageInfo中保存發送端的Id,消息序列號等。傳輸時需要它來標記消息。

8 cyber跨主機收發數據的qos

cyber可以在配置文件cyber/conf/cyber.conf中,給跨主機通信設置質量保證選項

這些選項讀入后保存在RoleAttributes的成員qos_profle中,這是一個QosProfile實例。

當創建fastrtps_Subscriber/fastrtps_Publisher時,這些選項會作為創建的參數傳入。

相關鏈接

百度 Apollo 8.0 Cyber 源代碼分析(一)
百度 Apollo 8.0 Cyber 源代碼分析(二)
百度 Apollo 8.0 Cyber 源代碼分析(三)
百度 Apollo 8.0 Cyber 源代碼分析(四)
百度 Apollo 8.0 Cyber 源代碼分析(五)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容