嵌入式軟件開發工程師談軟件架構的設計

嵌入式軟件開發工程師談軟件架構的設計

注:此處嵌入式特指基于linux平臺,單片機和其他rtos不在討論范圍

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

  • 近期有萌生換個行業方向的想法,想做做后臺服務器相關的開發,由于之前工作中并沒有這方面的實際需求,只是自己平時關注,了解了些知識,比如:NIO,epoll,ngnix,zeromq,libevent,libuv,高并發,分布式,redis,python,tornado,django,涉獵比較雜,都了解個皮毛,不精。意外的是屢屢被互聯網行業鄙視,面試機會都寥寥無幾。

  • 此時我想到底是什么問題呢,難道嵌入式出身的已經這么不受待見了嗎?想當初,嵌入式,驅動開發,可是趨之若鶩的行業(有點夸張,不過8,9年前嵌入式可是聽著比做java web的要牛逼些哦)

問題總是有原因的,我說下自己的理解:

嵌入式是否真的高大上之為什么沒有嵌入式軟件架構師?

  • 打開拉勾等招聘網站,搜索架構師,會出現各種系統架構師,web架構師,后臺服務端架構師等等,但是唯獨很難看到嵌入式軟件架構師。嵌入式軟件不需要架構嗎,驅動不需要架構嗎?答案是當然需要,不過為什么沒有這方面的職位?

    我的看法:目前國內的嵌入式開發主要分為嵌入式底層開發和嵌入式應用開發,嵌入式的底層開發一般叫做驅動開發,或者bsp開發,有時也有稱之為linux內核開發,名字聽著都很高大上的感覺。

    這么高大上的名字為什么沒有架構師呢:linux kernel的架構師是linus等一眾linux kernel開發維護者,因為本身linux kernel或者操作系統就是一個通用的平臺,解決通用的問題,linux開源屆的大牛都已經制定好了架構規則,留給可發揮的地方并不多,大部分工作只需要按照規則框架填充就可以了,而且以目前國內大部分公司的業務需求,只是在做外圍設備的集成,嵌入式平臺的porting,搭建裁剪,業務需求完全不會超過kernel里提供的功能范圍。導致沒有什么新的架構需要開發人員去設計,實現。那嵌入式bsp開發人員都在做什么:除了調試多種多樣的外設,替硬件擦屁股,就是解些穩定性的bug了(這里對具體工作不詳細描述了,調試外設只會增加一些經驗,增加廣度,對提高深度貢獻不大,只是按不會調試-》會調試-》調試的快這個路線發展,而解穩定性問題確實是需要一些積累經驗)

    而嵌入式上的應用開發,一般業務邏輯比較簡單,被很多人忽略,所以招聘方也會感覺沒有什么必要找架構師級別的了。

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

  • 但的確是這樣子的嗎?對于嵌入式底層的開發,有能力對kernel,驅動架構提出架構層優化的,國內的開發人員應該不多,所以對于大部分普通人,還是不要“妄想”做Linux kernel的架構師了(當然我相信國人中一定存在有這個能力的大牛),發現,解決一些bug,到更靠譜些。

那么對于嵌入式應用層的開發,我們真的不需要架構嗎?

  • 以自己的實際經歷講述下曾經對一個嵌入式設備應用軟件的架構設計和優化:
    筆者曾經接手過一個項目,項目采用單進程多線程的模型,項目中包括幾個模塊,以a, b, c, d,e代表。這個項目的業務邏輯決定這幾個模塊有不少關聯。
    例如:最初的設計中a模塊是一個狀態監測模塊,它會基于監測到的狀態調用b,c模塊的接口實現一些功能(多線程的好處就是直接調用很方便,所以開發人員大多這么干,簡單粗暴),但是需求總是千變萬化,加入一個f模塊,f模塊也需要對a模塊監測的狀態進行一個處理,按照之前的套路,完成這個功能分兩步:1,在f模塊提供個接口,2,在a模塊中調用該接口。至此新需求已經“完美”的解決了。

  • 前面提到需求總是千變萬化的,新的需求又來了,客戶提出定制需求,需要加入另一個g模塊,同樣處理a模塊監測的狀態,但是該定制需求不需要剛剛加入的f模塊,此時最簡單粗暴的方式是,定義一個宏,區分該定制需求和之前的通用需求,build兩個程序版本。這樣的做法看似簡單,但后面如果定制需求逐漸增多,維護這么多定制版本程序就是個噩夢,代碼管理和通用性也會是很大的問題,同時代碼中充斥著對不同宏定義的差異化處理,#ifdef xxx;do_something;#endif比較好的做法是加入設備型號版本的動態監測,用一個build程序版本動態支持所有的定制需求,這樣減少了對不同build程序的維護。但是這種做法只解決build程序的版本維護工作,沒有解決宏定義差異化處理的問題,只是會將之前的宏判斷,改為動態設備版本號判斷,如果這些差異化的判斷只集中在一處進行,也不會引起大的復雜化的問題,但顯然這個是不好保證,有可能這些差異化的處理會蔓延到整個項目的各個角落,這樣項目維護起來就會變成一場噩夢。

  • 不需要什么高深的軟件思想,大部人都會想到把差異化的部分提取出來,放在一個統一的地方集中管理,對差異化的修改只集中在這個統一管理的地方。
    通用做法就是采用callback設置鉤子,然后在callback中定制差異化的需求,對callback的處理做差異化的配置,對應到上面例子,就是在a模塊添加一個鉤子,然后在系統初始化時,根據設備版本號的不同,差異化定制callback處理函數,同時要將這些定制callback處理函數放在同一地方處理,否則仍然分散在各個角落里就沒有意義(前一種方式不放置鉤子是無法將這些差異化配置放在一起的),這樣處理帶來的另外一個好處是,我們對功能性需求的改變,不會影響到a模塊的處理,也就是我們添加功能,不需要修改a模塊的代碼了(前一種方式要修改a模塊的調用流程),這樣也就實現了一個模塊的分離。

  • 至此第二種的方案的架構(其實也談不上架構了)相比第一種方案已經有了不少提升,至少讓開發人員稍微輕松了些,對于其他定制需求,開發人員之需要修改這個callback處理,關注差異化部分就可以了。

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

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

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

  • 模塊的解耦:很多開發人員維護開發的多線程模型項目應該都多少會存在下面的問題:跨模塊間的直接調用,如果不相信,好,你的項目一定是分模塊的吧,現在隨機的刪掉一個模塊,build下看能build通過嗎(只需要build不需要運行),我相信大部分情況下一定會遇到某個函數調用,某個全局變量找不到的情況,這種情況說明你的模塊間存在強耦合了。由于多線程天然的優勢,地址空間的相互可見,導致直接調用十分容易,很多經驗尚淺的工程師,很容易就寫出直接調用的簡單粗暴的接口,如果遇到個static接口的函數,圖方便也會把static去掉,直接拿過來用了。這樣整個工程隨著功能不斷的添加,模塊間的交叉越來越多,耦合越高。

    而我之所以推崇多進程的原因就是,多進程能從物理上隔絕了這種“方便”的通訊方式,導致在想實現一個模塊交互時,會多思考下這個交互是必要的嗎,如果是必要的,則會進一步思考接口定義是否簡單明了(因為進程間的通訊相對會麻煩些,開發人員會本著能減少交互,明確接口的想法去仔細考慮接口,協議的定義,否則折騰的是自己了),這如同人生,如果一直順風順水,人們可能不會想太多,思考太多,而如果道路上有些坎坷,則會有另一種感悟吧。

    所以我的想法是多進程的模型會逼迫你去更多的思考想程序的設計,物理上減少模塊的耦合

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

  • 方便定位問題:多線程模型中當又一個線程異常退出,會導致整個進程退出,當然通過一些crash信息,可以定位是那個線程死掉,但如果這些線程模塊是由多個小組,人員維護,當整個進程崩潰掉后,如何判斷由那個小組解決,會是一個大的問題,而且有時還會出現的現象是掛在一個線程,但其實是另外一個線程模塊引起的(耦合的禍端),遇到這種情況,難免出現小組間的扯皮,推諉。(自信的工程師都認為我的代碼沒有問題)

    而如果采用多進程的模型,好吧,你的服務進程掛了,你自己找原因吧,沒什么可爭辯的了。

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

  • 分布式部署:互聯網行業一直強調的分布式,云啊什么的,嵌入式行業就很苦逼了,貌似不需要什么分布式吧,其實也對,大部分情況下,嵌入式采用單芯片,獨立運行,分布式遇到的很少。但如果萬一那天你在一個設備中,將本來一個芯片完成的功能分散到兩個芯片中處理呢,多進程的擴展就容易的多了。

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

  • 方便公司的代碼權限隔離:其實我鄙視這種做法,公司要相信自己的員工,但鑒于誠信在中國已經。。。。,做些隔離也無可厚非了。
    多線程模型下,前面講到如果去除一個模塊,你可能都不能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等機制,可以說大同小異,而難點在于協議的制定和性能處理的提升。

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

行文至此,只是想說明一個問題,軟件的設計是相通的,基于的思想是相同的,雖然嵌入式行業的業務邏輯相對比較簡單,但其實在仔細思考后,仍然會有很多架構上的改進,設計。

但是讓我感到悲哀的是,有些嵌入式開發者,鑒于業務邏輯的簡單,感覺采用一些不那么好的處理方式也能解決問題,不去思考如何去優化,改進。比如上面例子的方案一,如果在定制需求不多的情況下,維護起來也沒太大問題,即使定制需求多了,再招些初級程序員也能維護的過來,一個人一套代碼負責一個項目的公司也不是不存在。

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/449930.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/449930.shtml
英文地址,請注明出處:http://en.pswp.cn/news/449930.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

為何獲得風險投資的公司多數倒閉了?

北京時間12月12日消息,據國外媒體報道,哈佛商學院的高級講師施克哈爾高希(Shikhar Ghosh)指出,風險投資領域的失敗案例比媒體報道的要多得多。 高希的研究表明,在風險資本支持的公司之中,有75%的公司從未向投資者返回過…

Linux rm命令、Linux touch命令、Linux tee命令

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 Linux rm命令用于刪除一個文件或者目錄。 語法 rm [options] name...[options] name... 參數: -i 刪除前逐一詢問確認。…

highCharts使用中問題總結

想出一張圖表,數據是多條線體,45天的數據展示最近15天的。并且每次展示其中一條。可以切換時間看前面的 或者后面的。最開始遇到的問題是,展示一條線體成功,但是在切換線體的時候,成功但是接著刷新到最開始的狀態。跟同…

基于pjsip實現p2p語音對講

目的 為實現跨網絡的語音對講,使位于NAT后的兩個設備進行p2p的語音通訊,此處選用pjsip開源項目來實現。 未解決的問題:對稱型的NAT無法實現p2p打洞,pjsip采用turn服務進行轉發,不能稱之為純粹的p2p。 pjisp簡介 PJ…

VS2017 啟動調試報錯無法啟動程序 當前狀態中非法

昨天還可以使用,今天就莫名報了這個錯誤,百度了一下: 1. 第一種嘗試方法是右擊解決方案中的項目(圖標有帶球的),打開屬性選擇“WEB”選項,修改特定頁為Home,結果還是報錯。 2.我又關閉Windows防火墻,依舊報…

ABAP WRITE

1、空行 WRITE /. 2、AS CHECKBOX DATA: check1 TYPE c LENGTH 1 VALUE X, check2 TYPE c LENGTH 1 VALUE . START-OF-SELECTION. WRITE: / check1 AS CHECKBOX, Checkbox 1, / check2 AS CHECKBOX, Checkbox 2. AT LINE-SELECTION. READ: LINE 1 FIELD VALUE check1, LINE 2 …

java 星期幾、得周一、得周日、上周、下周、相差、日期工具類

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 import org.apache.commons.lang3.StringUtils;import java.text.ParseException; import java.text.SimpleDateFormat; import java.ut…

蘋果前CEO斯卡利變身創業導師:欲尋下個喬布斯

喬布斯和約翰斯卡利 導語:國外媒體今天撰文稱,蘋果前CEO約翰斯卡利(John Sculley)雖然早已離開蘋果,但仍然活躍在科技行業。他現在將目光轉向了醫療科技領域,希望從中挖掘出下一個喬布斯。 以下為文章全文: 下一個喬布…

記錄一次kernel內存泄漏的查找定位過程

Bug描述:壓力測試一個小工程時發現內存逐漸減少,10個小時后出現OOM Bug定位過程: 對整個工程模塊進行分解,逐步縮小范圍,由于整個工程包括幾個相對獨立的小模塊,而整個工程采用單進程多線程的模型&#x…

python3.6+selenium_Testsuits測試套件

#!/usr/bin/env python # -*- coding: utf-8 -*- # Time : 2018/11/23 11:23 # File : unittest_test2_2.pyTestsuits測試套件 import unittest from selenium import webdriver from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.commo…

PBRT筆記(7)——反射模型

基礎術語 表面反射可以分為4大類: diffuse 漫反射glossy specular 鏡面反射高光perfect specular 完美反射高光retro-reflective distributions 后反射分布幾何坐標系以及工具函數 pbrt中的反射是在反射坐標系中進行計算的。坐標系由著色點處法向量與兩個切向量組成…

Linux grep命令、Linux cd命令、Linux pwd命令

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 Linux grep命令用于查找文件里符合條件的字符串。 grep指令用于查找內容包含指定的范本樣式的文件,如果發現某文件的內容符…

忠告:創業公司與大公司正面競爭易死

移動互聯網的興起給了創業公司足夠的發展空間,也提供了一個相對公平的競爭平臺。但機遇永遠與挑戰并存。   對于初創公司,最大的挑戰莫過于在移動互聯網大潮中生存下來。IDG資本合伙人高翔認為,初創公司不要和大公司正面競爭,應…

海康螢石攝像機遠程監控機制分析

背景介紹 有一個遠程監控的需求,正常情況下采用服務器轉發視頻流對服務器要求很高,所以p2p看似是一個比較好的方案,但是p2p打洞卻不是一件容易的事情,所以打算分析下海康螢石攝像機遠程監控的機制,是否采用了p2p的方式…

實體類中存在List集合,怎么在xml文件中,對應

public class AppointmentDTO {/**訂單id**/private String appointmentId;/**訂單里面的商品list**/private List<CommodityShowInAppoinment> list;/**訂單里面的商品數量**/private Integer count;/**訂單實際付款的金額**/private Float totalMoney; } 怎么弄呢&…

git管理復雜項目代碼

背景 我初學前端的時候接觸git&#xff0c;那時候只要會add/commit什么的就好了&#xff0c;網上的教程大多都停留在從頭到尾一個個介紹git的命令&#xff0c;關于各種用法&#xff0c;特別是多個分支來回交叉沖突的實際處理&#xff0c;很少有這方面的介紹&#xff0c;經過很多…

實現一個通用的生產者消費者隊列(c語言版本)

背景&#xff1a;筆者之前一直從事嵌入式音視頻相關的開發工作&#xff0c;對于音視頻的數據的處理&#xff0c;生產者消費者隊列必不可少&#xff0c;而如何實現一個高效穩定的生產者消費者隊列則十分重要&#xff0c;不過按照筆者從業的經驗&#xff0c;所看到的現象&#xf…

美媒:小米新浪達成合作 采取行動對抗騰訊

來自美媒的報道稱&#xff0c;兩家中國最具發展潛力的科技公司&#xff0c;新浪和小米將會共同合作&#xff0c;結合各自的通信應用程序來共同對抗移動通信的挑戰&#xff0c;尤其是擁有2億用戶的強勁對手微信。 來自中國的消息稱&#xff0c;這次新浪與小米的合作將會涉及到新…

Linux expr命令、Linux wc命令、Linux let 命令

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 expr命令是一個手工命令行計數器&#xff0c;用于在UNIX/LINUX下求表達式變量的值&#xff0c;一般用于整數值&#xff0c;也可用于字符…

【English】六、am,is,are 分別用在什么地方

is&#xff1a;第三人稱單數am&#xff1a;第一人稱單數are&#xff1a;第二人稱單數&#xff0c;第一、二、三人稱的復數 用于第一人稱, I am ......(我是.......)用于第三人稱, He is ......(他是......) 或She is ......(她是......), It is ......(它是.......)用于第二人…