原本今晚想寫如何搞動態加載和動態補丁的,但很不幸,翻遍了硬盤,也沒找到之前的代碼,連網盤裏都沒備份。這時候,才煥然大悟--半年前我換上如今的筆記本,淘汰了那臺老掉牙的臺式機。所幸硬盤沒丟,不過一時時也無法讀裏面的數據了。等過些日子,讀出裏面的數據再談動態加載和動態補丁技術。今天說些簡單的,能在軟件設計中當即用得上的,模塊間通訊技術--統一消息。編程
統一消息模型,最先的啓發是UT的Wacos SSI。那是一個很不錯的通訊模型,容許模塊間的通訊統一成隊列通訊;而在物理上,模塊可能位於各類網絡中的不一樣的實體,又或者是不一樣的進程,線程。記得那會調試核心網的程序,在板卡上是沒有什麼調試環境的,除了WindShell(同CSHELL)外,就沒什麼支撐了。因而咱們就把軟件用GDB加載到目標機(無盤工做站),而後開始測試。有人不理解了,這沒啥啊!現實是這價值很大,大型系統的嵌入式開發,能爭取到的機房空間、設備和板卡老是奇缺,就當時的狀況來講,咱們三四我的才能分到一套設備。Wacos_SSI的隊列通訊技術,讓咱們能夠把目標機作成功能板塊,且只須要極少許的修改,就能和實際系統的主控板進行通信聯測,工做效率的提高自不待言。安全
再後來,哥在Nortel的時候知道了TIPC協議,好象是E///和IBM搗騰出來的東西。思路上,和Wacos SSI很接近。所不一樣的是,Wacos SSI在消息頭裏使用了IP地址,而TIPC則是自定義的節點地址,也所以包含了一個額外的節點地址和特定網絡間的地址翻譯過程。另一個區別是,Wacos SSI考慮了遠程節點間通訊和本地通訊的差異,只有遠程通訊時才傳遞消息實體,而本地則是傳遞標識(Handle)來快速完成。TIPC則沒講述這個層次的程序設計問題,也所以在工程實踐中應用寥寥。網絡
現現在,UT沒了,Nortel也沒了。特別是UT,十多年過去了,哥特別懷念那段日子,和個人那個團隊。無奈,哥就是災星,跟喜歡的公司相剋。不少局外人都說UT不咋的,就一個作小靈通的;可哥的眼裏,那的許多軟件開發團隊,戰鬥力一點不比Huawei差。就說哥作的網關城域交換機,才十來我的,而huawei是幾十人,好幾倍啊,最後市場表現仍是勢均力敵。固然,我仍是蠻佩服huawei的,他們的東西真心作的漂亮,維護界面人性化,不像咱們的,不少事情要命令行來實現。不過咱們也有特色,就是架構作的很是好,以致於客戶的需求,老是能很快實現,並且基本上對現有功能是0風險。呵呵,聽說氣死很多人!數據結構
這當中,有三大功臣:多線程
狀態機的代碼,已經在昨晚的內存泄漏裏的連接裏提供了,有興趣能夠下載或是用在喜歡的地方,哥只但願它有更多機會發揮價值。架構
嗯,Wacos SSI排在第一!是的,Wacos SSI的消息通訊讓咱們的系統變得很是柔性,模塊與模塊間幾乎沒有什麼複雜的耦合。想一想如今那些公司招聘需求裏,要求什麼多任務多線程編程能力,精通什麼信號量和同步技術,哥就想哭,這就是咱們的軟件水平,時刻準備着處在玩死本身。哥作程序,只考慮CPU有幾個線程核,至於系統有幾個進程線程,都是這個決定的,並且合併拆解任務,都是分分鐘能改代碼實現的事。跟哥一塊兒作軟件,就只要記住幾點:不管你和誰通訊,你只要知道他的地址,而後發消息給他就行了;而你也只要看着本身的隊列,有消息就幹活,沒消息就歇着。至於發消息,就一個標準的函數,而消息封裝格式,也是統一的。至於系統函數庫裏提供的什麼信號量,管道啥的,千萬別嘗試在應用裏面使用,不然,編譯器會用編譯錯誤來告訴你行不通。函數
有點扯遠了,回到正題。性能
統一消息的定義,包含兩個部分,消息標籤和消息頭,具體以下:測試
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結構是消息標籤,應用程序收、發消息時,都是收發的這個數據結構,以下:
int zMsgSend(zMsg_t *msg);
一般來講,咱們應該把這個消息標籤作的比較小,由於作的太大,來回複製它的內容是須要耗費CPU時間的。好比,你能夠將zAddr_t定義成word,zHandle_t定義成dword,這樣只須要8字節就夠了。不過記得字節對齊,通常來講,要保證長度是4的倍數。spa
消息頭就是消息內容的頭部格式段,除了這個頭部,剩下的就是應用自定義的payload部分。zMsgHdr_t和zMsgHdrEx_t實質上是同樣的。這裏面的地址部分,不是必須的,只有當消息透過網絡或是總線傳遞時,纔是必須的,不然無法由邊界模塊還原。而對於應用,如無特別約定,那幾個字節是無心義且內容不肯定的。
消息標籤和消息間是經過msgHandle關聯。這樣,當消息在本地傳遞時,msgHandle指向的是一塊普通內存;而當消息在本地進程間通訊時,則指向共享內存;至於網絡或是某個總線傳遞,邊界模塊負責本地內存數據和網絡數據間的轉換。如此一來,最大程度的減小實際消息體的拷貝開銷,讓消息傳遞變得高效,且細節處理對應用透明。
Wacos SSI的地址部分,填的是IP地址;固然,它還定義了一個模塊號來配合這個地址使用。整個通訊過程很簡單,應用只須要申請一個隊列,並告知SSI,這個隊列和哪一個目的模塊號使用。正常狀況下,這個作法都能知足需求,但碰上程序模塊從新規劃或是特俗測試目的,就有點力不從心了。所以,哥在zMsg_t標籤裏完全放棄了IP+module的地址組成,改成TIPC的地址方式。不過這也就讓系統必須維護一個路由表,用來完成特定目的地址到隊列的映射。
統一消息路由表定義以下:
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;
路由表項裏首先是地址,對應的是消息的目的地址。接下來是網絡地址和隊列地址,能夠有一個或是都有。
總上面的關係能夠看出,隊列和地址間的關係是一對多的關係,即多個地址的消息可能被投送到同一個隊列。這就讓模塊合併變得異常容易,固然,不安規則出牌的模塊何時什麼方法都白搭。一般來講,若是有IP網絡的通訊要求,系統就須要建立一個基礎的網絡邊界模塊。這個模塊自己可能並不須要地址,而只須要提供一個消息聚合的隊列。固然,在一個開放的網絡環境下,這個邊界模塊可能還須要作些安全性的工做,好比過濾非法消息等,這能夠經過在模塊內額外配置源IP地址,端口或是源目的地址等實現。若是遠端並不支持zMsg_t工做,則這時候的邊界模塊就須要作好消息的翻譯過程,爲遠端模塊分配映射模塊地址。固然,這些都是本地的,不屬於路由表內容。
從地址映射到真實的目的隊列或是網絡地址,是個頻繁的操做,設計上必需要很是高效。對於地址很是少的系統,好比總共才七八個模塊,能夠用一個緊湊的數據來作,簡單且不妨礙效率。但對於有數十或是上百個地址的系統來講,遍歷方法就不可取了。這時應該用二分搜索,或是平衡二叉樹。好比城域交換機,有十來塊子功能卡,每張卡上有十來個模塊,整個系統的地址空間有一百多,採用二分搜索,最多8次就夠了!相比消息處理函數的指令數,這部分開銷徹底能夠接受。而從另外一個角度來講,統一消息讓程序變得簡單可控,系統內減小了消息的拷貝操做,所帶來的系統效率和性能提高,遠遠大於查詢路由表的開銷。
當嵌入式世界有了統一消息後,哪些多線程的開發技巧還有很大價值麼?通常應用開發者真的須要理解這些知識麼?