【轉】嵌入式爲何沒有嵌入式軟件架構師?

    
 
                                                                                

此處嵌入式特指基於linux平臺,單片機和其餘rtos不在討論範圍~前端

 

我從事嵌入式軟件開發有6,7個年頭,bsp、驅動、應用軟件、android hall、framework等都有涉獵。平時除了關注嵌入式行業的發展,也多少對Web、後臺服務端、分佈式等方向的技術有一些關注。java

 

近期有萌生換個行業方向的想法,想作作後臺服務器相關的開發,因爲以前工做中並無這方面的實際需求,只是本身平時關注,瞭解了些知識,好比:NIO、epoll、ngnix、zeromq、libevent、libuv、高併發、分佈式、redis、python、tornado、django,涉獵比較雜,都瞭解個皮毛,不精。意外的是屢屢被互聯網行業鄙視,面試機會都寥寥無幾。python

 

此時我想究竟是什麼問題呢,難道嵌入式出身的已經這麼不受待見了嗎?想當初,嵌入式,驅動開發,但是趨之若鶩的行業(有點誇張,不過8,9年前嵌入式但是聽着比作java web的要牛逼些哦)。linux

 

問題老是有緣由的,我說下本身的理解:android

 

嵌入式是否真的高大上?爲何沒有嵌入式軟件架構師?程序員

 

打開各類招聘網站,搜索架構師,會出現各類系統架構師,web架構師,後臺服務端架構師等等,可是惟獨很難看到嵌入式軟件架構師。嵌入式軟件不須要架構嗎,驅動不須要架構嗎?答案是固然須要,不過爲何沒有這方面的職位?web

 

個人見解:目前國內的嵌入式開發主要分爲嵌入式底層開發和嵌入式應用開發,嵌入式的底層開發通常叫作驅動開發,或者bsp開發,有時也有稱之爲linux內核開發,名字聽着都很高大上的感受。面試

 

這麼高大上的名字爲何沒有架構師呢?linux、 kernel的架構師是linus等一衆linux kernel開發維護者,由於自己linux kernel或者操做系統就是一個通用的平臺,解決通用的問題,linux開源屆的大牛都已經制定好了架構規則,留給可發揮的地方並很少,大部分工做只須要按照規則框架填充就能夠了。redis

 

以目前國內大部分公司的業務需求,只是在作外圍設備的集成,嵌入式平臺的porting、搭建裁剪、業務需求徹底不會超過kernel裏提供的功能範圍,致使沒有什麼新的架構須要開發人員去設計、實現。那嵌入式bsp開發人員都在作什麼?除了調試多種多樣的外設,替硬件擦屁股,就是解些穩定性的bug了(這裏對具體工做不詳細描述了,調試外設只會增長一些經驗,增長廣度,對提升深度貢獻不大,只是按不會調試-》會調試-》調試的快這個路線發展,而解穩定性問題確實是須要一些積累經驗)django

 

而嵌入式上的應用開發,通常業務邏輯比較簡單,被不少人忽略,因此招聘方也會感受沒有什麼必要找架構師級別的了。

 

至此感受嵌入式行業的確不須要架構師,被互聯網行業的鄙視也沒什麼大驚小怪的。

 

但的確是這樣子的嗎?對於嵌入式底層的開發,有能力對kernel、驅動架構提出架構層優化的,國內的開發人員應該很少,因此對於大部分普通人,仍是不要「妄想」作Linux kernel的架構師了(固然我相信國人中必定存在有這個能力的大牛),發現,解決一些bug,到更靠譜些。

 

那麼對於嵌入式應用層的開發,咱們真的不須要架構嗎?

 

以本身的實際經歷講述下曾經對一個嵌入式設備應用軟件的架構設計和優化:我曾經接手過一個項目,項目採用單進程多線程的模型,項目中包括幾個模塊,以a, b, c, d,e表明。這個項目的業務邏輯決定這幾個模塊有很多關聯。

 

例如:最初的設計中a模塊是一個狀態監測模塊,它會基於監測到的狀態調用b,c模塊的接口實現一些功能(多線程的好處就是直接調用很方便,因此開發人員大多這麼幹,簡單粗暴)。可是需求老是變幻無窮,加入一個f模塊,f模塊也須要對a模塊監測的狀態進行一個處理,按照以前的套路,完成這個功能分兩步:

  1. 在f模塊提供接口。

  2. 在a模塊中調用該接口,至此新需求已經「完美」的解決了。

     

前面提到需求老是變幻無窮的,新的需求又來了,客戶提出定製需求,須要加入另外一個g模塊,一樣處理a模塊監測的狀態,可是該定製需求不須要剛剛加入的f模塊,此時最簡單粗暴的方式是,定義一個宏,區分該定製需求和以前的通用需求,build兩個程序版本。這樣的作法看似簡單,但後面若是定製需求逐漸增多,維護這麼多定製版本程序就是個噩夢,代碼管理和通用性也會是很大的問題,同時代碼中充斥着對不一樣宏定義的差別化處理。

 

比較好的作法是加入設備型號版本的動態監測,用一個build程序版本動態支持全部的定製需求,這樣減小了對不一樣build程序的維護。可是這種作法只解決build程序的版本維護工做,沒有解決宏定義差別化處理的問題,只是會將以前的宏判斷,改成動態設備版本號判斷,若是這些差別化的判斷只集中在一處進行,也不會引發大的複雜化的問題,但顯然這個很差保證,有可能這些差別化的處理會蔓延到整個項目的各個角落,這樣項目維護起來就會變成一場噩夢。

 

不須要什麼高深的軟件思想,大部人都會想到把差別化的部分提取出來,放在一個統一的地方集中管理,對差別化的修改只集中在這個統一管理的地方。

 

通用作法就是採用callback設置鉤子,而後在callback中定製差別化的需求,對callback的處理作差別化的配置,對應到上面例子,就是在a模塊添加一個鉤子,而後在系統初始化時,根據設備版本號的不一樣,差別化定製callback處理函數,同時要將這些定製callback處理函數放在同一地方處理,不然仍然分散在各個角落裏就沒有意義(前一種方式不放置鉤子是沒法將這些差別化配置放在一塊兒的),這樣處理帶來的另一個好處是,咱們對功能性需求的改變,不會影響到a模塊的處理,也就是咱們添加功能,不須要修改a模塊的代碼了(前一種方式要修改a模塊的調用流程),這樣也就實現了一個模塊的分離。

 

至此第二種的方案的架構(其實也談不上架構了)相比第一種方案已經有了很多提高,至少讓開發人員稍微輕鬆了些,對於其餘定製需求,開發人員之須要修改這個callback處理,關注差別化部分就能夠了。

 

軟件是須要不斷進化的,第二種方案是最優解嗎,固然不是,還有優化空間嗎?

 

下面先跑個題,談談多線程/多進程模型的優缺點,主要談多進程的優勢了:

教科書上的解釋就不提了,首先我對大的項目是推崇多進程模型,無關性能,主要緣由有:

 

1.模塊的解耦:不少開發人員維護開發的多線程模型項目應該都多少會存在下面的問題:跨模塊間的直接調用,若是不相信,好,你的項目必定是分模塊的吧,如今隨機的刪掉一個模塊,build下看能build經過嗎(只須要build不須要運行),我相信大部分狀況下必定會遇到某個函數調用,某個全局變量找不到的狀況,這種狀況說明你的模塊間存在強耦合了。

 

因爲多線程自然的優點,地址空間的相互可見,致使直接調用十分容易,不少經驗尚淺的工程師,很容易就寫出直接調用的簡單粗暴的接口,若是遇到個static接口的函數,圖方便也會把static去掉,直接拿過來用了。這樣整個工程隨着功能不斷的添加,模塊間的交叉愈來愈多,耦合越高。

 

而我之因此推崇多進程的緣由就是,多進程能從物理上隔絕了這種「方便」的通信方式,致使在想實現一個模塊交互時,會多思考下這個交互是必要的嗎,若是是必要的,則會進一步思考接口定義是否簡單明瞭(由於進程間的通信相對會麻煩些,開發人員會本着能減小交互,明確接口的想法去仔細考慮接口,協議的定義,不然折騰的是本身了),這如同人生,若是一直順風順水,人們可能不會想太多,思考太多,而若是道路上有些坎坷,則會有另外一種感悟吧。

 

因此個人想法是多進程的模型會逼迫你去更多的思考想程序的設計,物理上減小模塊的耦合。

 

抽象通用組件,分離通用功能和業務邏輯功能:當把一個多線程模型修改成多進程模型的過程當中,常常會發現有些接口代碼重複的出如今多個進程模塊中,由於以前接口函數是在一個進程空間,你們均可以直接調用的,好比接口A被模塊a,b調用,模塊a,b分離爲兩個獨立的進程後,接口A須要在a,b中分別實現了,無需解釋,重複代碼這個在軟件工程中是大忌,必須消除。作法也很簡單,將這些被多個模塊調用的接口分離處理作成lib,供其餘模塊調用,當你完成這部分工做後,你發現了什麼,是否是剝離的接口,能夠做爲整個項目的通用組件存在了,完美的狀況下,lib下的代碼是通用基礎組件,各個模塊中是獨立的業務處理模塊。

 

2.方便定位問題:多線程模型中當又一個線程異常退出,會致使整個進程退出,固然經過一些crash信息,能夠定位是哪一個線程死掉。但若是這些線程模塊是由多個小組、人員維護,當整個進程崩潰掉後,如何判斷由哪一個小組解決,會是一個大的問題。並且有時還會出現的現象是掛在一個線程,但實際上是另一個線程模塊引發的(耦合的禍端),遇到這種狀況,不免出現小組間的扯皮,推諉。(自信的工程師都認爲個人代碼沒有問題)

 

而若是採用多進程的模型,好吧,你的服務進程掛了,你本身找緣由吧,沒什麼可爭辯的了。

 

3.方便性能測試:多線程種單個線程的資源佔用不是很好查看(至少有些嵌入式系統沒有完善的命令),當整個進程資源消耗很高時,如何判判定位時哪一個模塊線程的問題,同前邊問題同樣難以抉擇。而若是是多進程的模型,誰的進程佔了好多資源,誰就去查下吧,其實這個仍是個顆粒度的問題。一樣的系統,劃分紅多個進程,單個進程的複雜度必定比只有一個進程的複雜度低的多,複雜度下降,也就更容易定位查找各類問題。

 

4.分佈式部署:互聯網行業一直強調的分佈式,雲啊什麼的,嵌入式行業就很苦逼了,貌似不須要什麼分佈式吧,其實也對,大部分狀況下,嵌入式採用單芯片,獨立運行,分佈式遇到的不多。但若是萬一那天你在一個設備中,將原本一個芯片完成的功能分散到兩個芯片中處理呢,多進程的擴展就容易的多了。

 

這只是舉個特殊的例子,其實嵌入式設備就是個分佈式的行業,只是一開始就已經實現分離了,而不是從集中到分佈式的路線發展起來的。

 

方便公司的代碼權限隔離:其實我鄙視這種作法,公司要相信本身的員工,但鑑於誠信在中國已經。。。作些隔離也無可厚非了。

 

多線程模型下,前面講到若是去除一個模塊,你可能都不能build了,那麼是要把全部代碼暴露給全部的工程師嗎,顯然不能,因此各個模塊只能提供庫的形式了,不過我以爲將通用功能接口組織成通用庫是正常的作法,而若是把和業務相關的模塊也提供成庫,就有點。。。。

 

至此在補充一下,以上全部的優勢,其實都不是很關鍵的點,都不可以讓多進程有絕對的優點壓倒多線程模型,只是從我的的角度以爲,多進程模型更能強迫工程師思考解決一些問題。(而這些問題有經驗的工程師不管什麼模型都會思考的)

 

上面說了這麼多,該考慮下把以前項目的例子改爲多進程模型,不然就只是紙上談兵了。

 

首當其衝的問題就是:選擇多進程的通信方式,多線程間的直接調用是不能用了,那麼如何選擇多進程的通信方式呢?

 

linux下提供不少ipc方式,此處不一一列舉,對於非大數據量的控制,通信消息的傳遞,比較好的方式是採用socket,本機上更多采用unix socket方式,(這種方式有什麼好處?當你有須要把單一系統作成分佈式系統時,優點就明顯了)

 

可是僅僅採用socket來實現前面例子的功能,一樣會存在一些問題:仍是前面的例子,首先說明前面咱們優化後的第二種方案在多進程模型已經不能在繼續使用了,緣由比較簡單,應該不須要解釋。

 

簡單的作法即基於方案一,把直接調用改成socket通訊(定義好通訊協議便可),可是熟悉socket開發的工程師都清楚,開始socket通訊要先進行一些前期的工做(主要就是鏈接,將兩個模塊關聯起來),因此前面的例子會變成這個樣子,模塊a要和模塊b,c創建鏈接,若是加入f模塊,模塊a還要和f模塊創建鏈接。這樣狀況在內心畫一張鏈接圖就會發現好像咱們織了一張蜘蛛網,節點間的關係錯綜複雜,並且和方案一同樣,咱們添加一個和a關聯的模塊,就要修改模塊a的代碼,並且這種狀況比多線程模型還有繁瑣複雜的多了。這種作法絕對是個噩夢。

 

如何解決?我想不少人必定想到了採用總線分發的方式。瞭解android系統開發的會想到binder,瞭解openwrt的會想到ubus,瞭解桌面會想到dbus,互聯網行業的開發者必定也知道redis裏提供的sub/pub模塊。

 

上面的binder,ubus等原理很簡單,就是創建一個消息中心,構建一個轉發路由模型,全部其餘模塊之間不直接交互,而是採用消息中心轉發,路由,而如何決定路由規則,則採用訂閱/發佈的觀察者模式來進行規則的定義。(嵌入式開發或者c語言開發者,常常會誤覺得設計模式是和麪向對象語言關聯的,是面嚮對象語言獨有,雖然有不少大牛作了這方面的普及,但鑑於有些開發者的信息渠道比較閉塞,致使這種想法仍然十分盛行)

 

基於這個模型,咱們上面例子的需求就很好解決了,加入一個消息中心模塊,全部須要通訊的模塊只同該消息中心模塊鏈接,而後訂閱本身感興趣的事件,當事件發生時,只須要進行相應的處理就能夠了。

 

這樣上面的模塊b,c訂閱模塊a的事件,當模塊a檢測到某事件時,發佈該事件,該事件先到達消息中心,在由消息中心轉發給模塊b,c,而對於新加入的模塊f,也只須要訂閱該模塊,而不須要在修改到模塊a的代碼,使功能的擴展十分方便。

 

同時對於前面提到的定製化開發一樣獲得了簡化,若是定製化版本須要加入模塊g,這樣只須要定製化版本中將模塊g做爲一個獨立進程啓動,而後訂閱模塊a的事件便可,而定製版本和通用版的區別就在因而否啓動模塊g的進程,從而實現了軟件工程的一個目標:功能的添加如同搭積木同樣,只須要把一個模塊插入(啓動)或拔出(不啓動)便可,功能的改變只侷限在一個或某幾個模塊間,對主體框架不會有任何影響。

 

以上大概描述了對一個項目需求逐步優化的過程,例子看似是基於嵌入式項目,但貌似對軟件工程一樣適用。

 

來到互聯網行業:

查看下各大網站架構師對本網站技術架構變革分享的文章,首先提到的通常都是,基於業務將以前的一個應用服務器功能拆分,更加細化(好比電商對登陸,註冊,交易,商品,賣家等業務服務的拆分),而後將拆分出來的服務部署在多臺服務器上,來提供併發。這裏是否有些耳熟,和前面講到的多線程到多進程的劃分是否有類似呢。

 

拆分後一樣遇到通訊的問題,此時不少消息中間件應運而生,好比阿里的duboo,簡單瞭解下這些中間件的原理,無外乎訂閱發佈,RPC等機制,能夠說大同小異,而難點在於協議的制定和性能處理的提高。

 

在對照下互聯網行業的負載均衡方案,彷彿那個負載均衡的前端也像一個消息中心了。

 

上面說了這麼多,只是想說明一個問題,軟件的設計是相通的,基於的思想是相同的,雖然嵌入式行業的業務邏輯相對比較簡單,但其實在仔細思考後,仍然會有不少架構上的改進,設計。

 

可是讓我感到悲哀的是,有些嵌入式開發者,鑑於業務邏輯的簡單,感受採用一些不那麼好的處理方式也能解決問題,不去思考如何去優化,改進。好比上面例子的方案一,若是在定製需求很少的狀況下,維護起來也沒太大問題,即便定製需求多了,再招些初級程序員也能維護的過來,一我的一套代碼負責一個項目的公司也不是不存在。

 

一樣互聯網行業和嵌入式行業也不該該存在一個不能夠逾越的高牆,咱們更應該關注的是通用的軟件工程思想。 

相關文章
相關標籤/搜索