標(biāo)準(zhǔn)化模塊接口--統(tǒng)一消息
點擊上方「嵌入式大雜燴」,選擇「置頂公眾號」第一時間查看嵌入式筆記!
鏈接:https://www.cnblogs.com/hhao020/p/5018951.html
本來今晚想寫如何搞動態(tài)加載和動態(tài)補丁的,但很不幸,翻遍了硬盤,也沒找到以前的代碼,連網(wǎng)盤里都沒備份。這時候,才煥然大悟--半年前我換上現(xiàn)在的筆記本,淘汰了那臺老掉牙的臺式機(jī)。
所幸硬盤沒丟,不過一時時也沒法讀里面的數(shù)據(jù)了。等過些日子,讀出里面的數(shù)據(jù)再談動態(tài)加載和動態(tài)補丁技術(shù)。今天說些簡單的,能在軟件設(shè)計中立即用得上的,模塊間通信技術(shù)--統(tǒng)一消息。
統(tǒng)一消息模型,最早的啟發(fā)是UT的Wacos SSI。那是一個很不錯的通信模型,允許模塊間的通信統(tǒng)一成隊列通信;而在物理上,模塊可能位于各種網(wǎng)絡(luò)中的不同的實體,又或者是不同的進(jìn)程,線程。
記得那會調(diào)試核心網(wǎng)的程序,在板卡上是沒有什么調(diào)試環(huán)境的,除了WindShell(同CSHELL)外,就沒什么支撐了。
于是我們就把軟件用GDB加載到目標(biāo)機(jī)(無盤工作站),然后開始測試。有人不理解了,這沒啥??!現(xiàn)實是這價值很大,大型系統(tǒng)的嵌入式開發(fā),能爭取到的機(jī)房空間、設(shè)備和板卡總是奇缺,就當(dāng)時的情況來說,我們?nèi)膫€人才能分到一套設(shè)備。
Wacos_SSI的隊列通信技術(shù),讓我們可以把目標(biāo)機(jī)做成功能板塊,且只需要極少量的修改,就能和實際系統(tǒng)的主控板進(jìn)行通訊聯(lián)測,工作效率的提升自不待言。
再后來,哥在Nortel的時候知道了TIPC協(xié)議,好象是E///和IBM搗騰出來的東西。思路上,和Wacos SSI很接近。所不同的是,Wacos SSI在消息頭里使用了IP地址,而TIPC則是自定義的節(jié)點地址,也因此包含了一個額外的節(jié)點地址和特定網(wǎng)絡(luò)間的地址翻譯過程。
另外一個區(qū)別是,Wacos SSI考慮了遠(yuǎn)程節(jié)點間通信和本地通信的差別,只有遠(yuǎn)程通信時才傳遞消息實體,而本地則是傳遞標(biāo)識(Handle)來快速完成。TIPC則沒講述這個層次的程序設(shè)計問題,也因此在工程實踐中應(yīng)用寥寥。
現(xiàn)如今,UT沒了,Nortel也沒了。特別是UT,十多年過去了,哥特別懷念那段日子,和我的那個團(tuán)隊。無奈,哥就是災(zāi)星,跟喜歡的公司相克。很多局外人都說UT不咋的,就一個做小靈通的;可哥的眼里,那的許多軟件開發(fā)團(tuán)隊,戰(zhàn)斗力一點不比Huawei差。
就說哥做的網(wǎng)關(guān)城域交換機(jī),才十來個人,而huawei是幾十人,好幾倍啊,最后市場表現(xiàn)還是平分秋色。當(dāng)然,我還是蠻佩服huawei的,他們的東西真心做的漂亮,維護(hù)界面人性化,不像我們的,很多事情要命令行來實現(xiàn)。不過我們也有特點,就是架構(gòu)做的非常好,以至于客戶的需求,總是能很快實現(xiàn),而且基本上對現(xiàn)有功能是0風(fēng)險。呵呵,據(jù)說氣死不少人!
這當(dāng)中,有三大功臣:
-
Wacos SSI; -
狀態(tài)機(jī); -
數(shù)據(jù)驅(qū)動模型。
狀態(tài)機(jī)的代碼,已經(jīng)在昨晚的內(nèi)存泄漏里的鏈接里提供了,有興趣可以下載或是用在喜歡的地方,哥只希望它有更多機(jī)會發(fā)揮價值。
嗯,Wacos SSI排在第一!是的,Wacos SSI的消息通信讓我們的系統(tǒng)變得非常柔性,模塊與模塊間幾乎沒有什么復(fù)雜的耦合。想想現(xiàn)在那些公司招聘需求里,要求什么多任務(wù)多線程編程能力,精通什么信號量和同步技術(shù),哥就想哭,這就是我們的軟件水平,時刻準(zhǔn)備著處在玩死自己。
哥做程序,只考慮CPU有幾個線程核,至于系統(tǒng)有幾個進(jìn)程線程,都是這個決定的,而且合并拆解任務(wù),都是分分鐘能改代碼實現(xiàn)的事。
跟哥一起做軟件,就只要記住幾點:無論你和誰通信,你只要知道他的地址,然后發(fā)消息給他就好了;而你也只要看著自己的隊列,有消息就干活,沒消息就歇著。
至于發(fā)消息,就一個標(biāo)準(zhǔn)的函數(shù),而消息封裝格式,也是統(tǒng)一的。至于系統(tǒng)函數(shù)庫里提供的什么信號量,管道啥的,千萬別嘗試在應(yīng)用里面使用,否則,編譯器會用編譯錯誤來告訴你行不通。
有點扯遠(yuǎn)了,回到正題。
統(tǒng)一消息的定義,包含兩個部分,消息標(biāo)簽和消息頭,具體如下:
typedef?struct?_MSG_TAG_TYPE_
{
zAddr_t?srcAddr;
zAddr_t?dstAddr;
zHandle_t?msgHandle;
}?PACKED?zMsg_t;
typedef?struct?_MSG_HEAD_TYPE_
{
byte_t?sysrsvd[8];?//reserved?for?adding?src?&?dst?addresses?on?network.?
word_t?msgLen;
word_t?msgId;?
dword_t?srcInst;
dword_t?dstInst;
}?PACKED?zMsgHdr_t;
typedef?struct?_MSG_HEAD_EX_TYPE_
{
zAddr_t?srcAddr;
zAddr_t?dstAddr;?
word_t?msgLen;
word_t?msgId;?
dword_t?srcInst;
dword_t?dstInst;
byte_t?msgBuf[1];
}?PACKED?zMsgHdrEx_t;
zMsg_t結(jié)構(gòu)是消息標(biāo)簽,應(yīng)用程序收、發(fā)消息時,都是收發(fā)的這個數(shù)據(jù)結(jié)構(gòu),如下:
int?zMsgSend(zMsg_t?*msg);
通常來說,我們應(yīng)該把這個消息標(biāo)簽做的比較小,因為做的太大,來回復(fù)制它的內(nèi)容是需要耗費CPU時間的。比如,你可以將zAddr_t定義成word,zHandle_t定義成dword,這樣只需要8字節(jié)就夠了。不過記得字節(jié)對齊,一般來說,要保證長度是4的倍數(shù)。
消息頭就是消息內(nèi)容的頭部格式段,除了這個頭部,剩下的就是應(yīng)用自定義的payload部分。zMsgHdr_t和zMsgHdrEx_t實質(zhì)上是一樣的。這里面的地址部分,不是必須的,只有當(dāng)消息透過網(wǎng)絡(luò)或是總線傳遞時,才是必須的,否則沒法由邊界模塊還原。而對于應(yīng)用,如無特別約定,那幾個字節(jié)是無意義且內(nèi)容不確定的。
消息標(biāo)簽和消息間是通過msgHandle關(guān)聯(lián)。這樣,當(dāng)消息在本地傳遞時,msgHandle指向的是一塊普通內(nèi)存;而當(dāng)消息在本地進(jìn)程間通信時,則指向共享內(nèi)存;至于網(wǎng)絡(luò)或是某個總線傳遞,邊界模塊負(fù)責(zé)本地內(nèi)存數(shù)據(jù)和網(wǎng)絡(luò)數(shù)據(jù)間的轉(zhuǎn)換。如此一來,最大程度的減少實際消息體的拷貝開銷,讓消息傳遞變得高效,且細(xì)節(jié)處理對應(yīng)用透明。
Wacos SSI的地址部分,填的是IP地址;當(dāng)然,它還定義了一個模塊號來配合這個地址使用。整個通信過程很簡單,應(yīng)用只需要申請一個隊列,并告知SSI,這個隊列和哪個目的模塊號使用。
正常情況下,這個做法都能滿足需求,但碰上程序模塊重新規(guī)劃或是特俗測試目的,就有點力不從心了。因此,哥在zMsg_t標(biāo)簽里徹底放棄了IP+module的地址組成,改為TIPC的地址方式。不過這也就讓系統(tǒng)必須維護(hù)一個路由表,用來完成特定目的地址到隊列的映射。
統(tǒng)一消息路由表定義如下:
typedef?struct?Z_UDP_ADDR_TYPE
{
dword_t?ip;
word_t?port;
}?zUDPAddr_t;
typedef?struct?Z_MSGQ_ADDR_TYPE
{
void?*qid;
}?zQueAddr_t;
typedef?struct?Z_MSGQ_OUT_TYPE
{
zAddr_t?addr;?
zUDPAddr_t?udpAddr;
zQueAddr_t?queAddr;?
}?zMsgRoute_t;
路由表項里首先是地址,對應(yīng)的是消息的目的地址。接下來是網(wǎng)絡(luò)地址和隊列地址,可以有一個或是都有。
-
僅隊列地址:說明是本地(或者是需要經(jīng)隱形邊界代理轉(zhuǎn)發(fā))的消息,目的地址為隊列所有者; -
僅網(wǎng)絡(luò)地址:說明是遠(yuǎn)程消息,且應(yīng)該直接網(wǎng)絡(luò)發(fā)送,無需經(jīng)過邊界代理,目的地址為遠(yuǎn)端模塊地址; -
含兩類地址:遠(yuǎn)程消息,應(yīng)用發(fā)送時通過隊列地址送入邊界模塊,再通過網(wǎng)絡(luò)地址發(fā)送,,目的地址為遠(yuǎn)端模塊地址。
總上面的關(guān)系可以看出,隊列和地址間的關(guān)系是一對多的關(guān)系,即多個地址的消息可能被投送到同一個隊列。
這就讓模塊合并變得異常容易,當(dāng)然,不安規(guī)則出牌的模塊什么時候什么方法都白搭。通常來說,如果有IP網(wǎng)絡(luò)的通信要求,系統(tǒng)就需要創(chuàng)建一個基礎(chǔ)的網(wǎng)絡(luò)邊界模塊。
這個模塊本身可能并不需要地址,而只需要提供一個消息聚合的隊列。當(dāng)然,在一個開放的網(wǎng)絡(luò)環(huán)境下,這個邊界模塊可能還需要做些安全性的工作,比如過濾非法消息等,這可以通過在模塊內(nèi)額外配置源IP地址,端口或是源目的地址等實現(xiàn)。
如果遠(yuǎn)端并不支持zMsg_t工作,則這時候的邊界模塊就需要做好消息的翻譯過程,為遠(yuǎn)端模塊分配映射模塊地址。當(dāng)然,這些都是本地的,不屬于路由表內(nèi)容。
從地址映射到真實的目的隊列或是網(wǎng)絡(luò)地址,是個頻繁的操作,設(shè)計上必須要非常高效。對于地址非常少的系統(tǒng),比如總共才七八個模塊,可以用一個緊湊的數(shù)據(jù)來做,簡單且不妨礙效率。但對于有數(shù)十或是上百個地址的系統(tǒng)來說,遍歷方法就不可取了。這時應(yīng)該用二分搜索,或是平衡二叉樹。
比如城域交換機(jī),有十來塊子功能卡,每張卡上有十來個模塊,整個系統(tǒng)的地址空間有一百多,采用二分搜索,最多8次就夠了!相比消息處理函數(shù)的指令數(shù),這部分開銷完全可以接受。而從另一個角度來說,統(tǒng)一消息讓程序變得簡單可控,系統(tǒng)內(nèi)減少了消息的拷貝操作,所帶來的系統(tǒng)效率和性能提升,遠(yuǎn)遠(yuǎn)大于查詢路由表的開銷。
當(dāng)嵌入式世界有了統(tǒng)一消息后,哪些多線程的開發(fā)技巧還有很大價值么?一般應(yīng)用開發(fā)者真的需要理解這些知識么?
溫馨提示
由于微信公眾號近期改變了推送規(guī)則,如果您想經(jīng)??吹轿覀兊奈恼?,可以在每次閱讀后,在頁面下方點一個「贊」或「在看」,這樣每次推送的文章才會第一時間出現(xiàn)在您的訂閱列表里。
版權(quán)聲明:本文來源網(wǎng)絡(luò),免費傳達(dá)知識,版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請聯(lián)系我進(jìn)行刪除。
猜你喜歡:
實踐 | AP3216三合一整合型光感測器實驗分享
實用 | 一個高性能通信庫的簡單使用分享
不按套路出牌,這么來寫IIC驅(qū)動?
干貨 | C語言實現(xiàn)面向?qū)ο缶幊蹋ǜ酱a)
2020年精選原創(chuàng)筆記匯總
在公眾號聊天界面回復(fù)1024,可獲取嵌入式資源;回復(fù)?m?,可查看文章匯總。
文章都看完了不點個嗎
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!