加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 1、問(wèn)題背景
    • 2、軟件架構(gòu)問(wèn)題
    • 3、軟件問(wèn)題的分析與解決
    • 4、非技術(shù)性問(wèn)題
    • 5、結(jié)語(yǔ)
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

嵌入式軟件的問(wèn)題分析

2023/04/27
2311
閱讀需 28 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

1、問(wèn)題背景

一切為了進(jìn)度,軟件開發(fā)的首要目標(biāo)就是以最快的速度滿足客戶需求,“快”是第一要素,但是短期指標(biāo);可復(fù)用性、擴(kuò)展性等長(zhǎng)期指標(biāo)被忽略,導(dǎo)致后期的維護(hù)、功能增減調(diào)整都非常困難。一個(gè)小的業(yè)務(wù)需求會(huì)牽一發(fā)而動(dòng)全身,一個(gè)小的故障修復(fù)可能引入更多的問(wèn)題。整個(gè)系統(tǒng)包袱越來(lái)越沉重,軟件的質(zhì)量和開發(fā)周期越來(lái)越不可控。

排除軟件開發(fā)人員的水平和項(xiàng)目進(jìn)度的原因,主要影響因素還包括軟件架構(gòu),和軟件缺陷的修復(fù)能力。對(duì)于量產(chǎn)軟件,架構(gòu)問(wèn)題是先天性的,后期很難大改,只能前期預(yù)防;軟件缺陷問(wèn)題是無(wú)法避免的,只能期望快速修復(fù)。拋磚引玉,也可先參看《嵌入式軟件bug從哪來(lái),怎么去》。

2、軟件架構(gòu)問(wèn)題

2.1 軟件架構(gòu)的特點(diǎn)

1. 承載力

正如一艘船最多能裝多少人,從軟件方面來(lái)說(shuō)是軟件架構(gòu)能承載多少業(yè)務(wù)或功能需求,當(dāng)然,這需要架構(gòu)師一開始架構(gòu)系統(tǒng)的時(shí)候,就需要有一定的預(yù)見性。但也沒(méi)必要為了極小概率事件增加過(guò)多的冗余。

2. 易用性

易用性決定了軟件的整體開發(fā)效率,好的架構(gòu)會(huì)讓團(tuán)隊(duì)成員容易上手,子系統(tǒng)容易對(duì)接,開發(fā)效率高,各模塊和子系統(tǒng)的編寫只需要關(guān)注系統(tǒng)的設(shè)計(jì)和編碼工作,其他模塊間通信方面的事情架構(gòu)可以提供很好的兼容。

3. 擴(kuò)展性

一個(gè)水杯除了用來(lái)喝水,也可用來(lái)喝酒,適應(yīng)不同場(chǎng)景,在一定范圍內(nèi)滿足不同的需求,是非常有必要的。軟件架構(gòu)也是這樣,要新增更多的功能就要具備更高的擴(kuò)展性??蓴U(kuò)展性的關(guān)鍵就在于新增部分不能影響其他,如果增刪導(dǎo)致系統(tǒng)整體使用異常,那么這個(gè)架構(gòu)的可擴(kuò)展性就很差。

4. 伸縮性

伸縮性就是設(shè)計(jì)的方案或系統(tǒng)是否可以根據(jù)需求適配不同數(shù)量的功能或子系統(tǒng),在我們?cè)O(shè)計(jì)的軟件系統(tǒng)中,架構(gòu)的可伸縮性決定了架構(gòu)的可適配性,例如,當(dāng)硬件資源不足時(shí),可以調(diào)整配置如flash的空間分配,支持減少一些服務(wù)但仍能正常運(yùn)行。

5. 容錯(cuò)性

軟件運(yùn)行中的異常,如用戶的非法操作,或者軟件本身的小缺陷導(dǎo)致整個(gè)系統(tǒng)無(wú)法使用,那這個(gè)架構(gòu)容錯(cuò)性就很差。軟件中的一些缺陷無(wú)法避免,但是我們應(yīng)盡量保證這個(gè)缺陷的影響范圍最小。倘若出現(xiàn)系統(tǒng)無(wú)法使用的情況,應(yīng)該有備份方案,比如自動(dòng)重啟或者自動(dòng)恢復(fù)數(shù)據(jù)等功能,也應(yīng)該能夠讓開發(fā)人員及時(shí)知道問(wèn)題的發(fā)生,以及問(wèn)題所在的位置并記錄錯(cuò)誤信息。

在架構(gòu)設(shè)計(jì)中,以上五項(xiàng)基本能力缺一不可,某項(xiàng)能力的突出并不能帶動(dòng)其他項(xiàng),如果某一項(xiàng)能力比較弱,隨著時(shí)間的推移,問(wèn)題會(huì)越來(lái)越大,甚至系統(tǒng)崩潰。就像木桶原理那樣,一個(gè)木桶的容量不是取決于最長(zhǎng)的那根木板,而是取決于最短的那根。

2.2 如何規(guī)劃軟件架構(gòu)

2.2.1 必須熟悉業(yè)務(wù)

軟件是為業(yè)務(wù)服務(wù)的,業(yè)務(wù)才是“目的”,軟件系統(tǒng)是為了達(dá)成業(yè)務(wù)系統(tǒng)目標(biāo)的手段和方法。適應(yīng)當(dāng)前的業(yè)務(wù)需求是基礎(chǔ),充分考慮和預(yù)測(cè)未來(lái)的業(yè)務(wù)擴(kuò)展,根據(jù)業(yè)務(wù)的擴(kuò)展性來(lái)設(shè)計(jì)軟件的擴(kuò)展性。如果可預(yù)見未來(lái)沒(méi)有擴(kuò)展重大新業(yè)務(wù)的需求,那么相應(yīng)的軟件架構(gòu)就沒(méi)有必要采用高擴(kuò)展的軟件架構(gòu)。比如嵌入式的傳感器數(shù)據(jù)采集小設(shè)備,就沒(méi)有必要把云計(jì)算等,業(yè)務(wù)范圍不沾邊的技術(shù)點(diǎn)放到其中。軟件架構(gòu)必須以服務(wù)業(yè)務(wù)為核心思想,不熟悉當(dāng)前軟件業(yè)務(wù)、和未來(lái)業(yè)務(wù)的擴(kuò)展的架構(gòu)師是很難設(shè)計(jì)出好的軟件架構(gòu)。

2.2.1 借鑒業(yè)內(nèi)成熟的架構(gòu)

不照搬,并不意味著不要借鑒。借鑒業(yè)內(nèi)成熟的軟、硬件架構(gòu)是相對(duì)穩(wěn)妥、高效的做法。以業(yè)內(nèi)的架構(gòu)為基礎(chǔ),根據(jù)自身業(yè)務(wù)的特點(diǎn),進(jìn)行適配、裁剪和增加新的功能。熟悉業(yè)內(nèi)常規(guī)的、成熟的、最新的軟件架構(gòu)是架構(gòu)師的一項(xiàng)基本功。但熟悉并不是意味著必須立即在目標(biāo)系統(tǒng)中實(shí)施這些軟件架構(gòu)。

2.2.3 采用設(shè)計(jì)模塊

設(shè)計(jì)模式(Design pattern)代表了最佳實(shí)踐,設(shè)計(jì)模式是軟件開發(fā)人員在開發(fā)過(guò)程中對(duì)一般問(wèn)題的解決方案;是一套被反復(fù)使用的、多數(shù)人知曉的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié),經(jīng)過(guò)相當(dāng)長(zhǎng)的一段時(shí)間的試驗(yàn)和錯(cuò)誤總結(jié)出來(lái)的。

使用設(shè)計(jì)模式是為了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性,合理地運(yùn)用設(shè)計(jì)模式可以完美地解決很多問(wèn)題。每種模式在現(xiàn)實(shí)中都有相應(yīng)的原理來(lái)與之對(duì)應(yīng),每種模式都描述了一個(gè)在我們周圍不斷重復(fù)發(fā)生的問(wèn)題,以及該問(wèn)題的核心解決方案,這也是設(shè)計(jì)模式能被廣泛應(yīng)用的原因。

用設(shè)計(jì)模式構(gòu)建一個(gè)新的軟件模塊時(shí),短期會(huì)讓人感覺(jué)有多此一舉的味道;但中長(zhǎng)期來(lái)看,設(shè)計(jì)模式能夠克服“壞”架構(gòu)的特征。學(xué)習(xí)這些模式有助于經(jīng)驗(yàn)不足的開發(fā)人員通過(guò)一種簡(jiǎn)單快捷的方式來(lái)學(xué)習(xí)軟件設(shè)計(jì)。盡管設(shè)計(jì)模塊通常被有經(jīng)驗(yàn)的面向?qū)ο蟮能浖_發(fā)人員所采用,但是嵌入式軟件C語(yǔ)言也可以借鑒,參考《嵌入式軟件的設(shè)計(jì)模式(上),《嵌入式軟件的設(shè)計(jì)模式(下)》。

2.2.4 合理的橫向和縱向切分

橫向切分 :從硬件、驅(qū)動(dòng)、組件到業(yè)務(wù)層,軟件分層隔離。如數(shù)據(jù)通信:PHY/MAC/IP/TCP/應(yīng)用層

縱向切分 :根據(jù)業(yè)務(wù)處理流程的環(huán)節(jié)縱向切分,不同的環(huán)節(jié)為不同的模塊,不同的業(yè)務(wù)功能為不同的模塊,如socket網(wǎng)絡(luò)、GNSS衛(wèi)星定位。

2.2.5 按樹形結(jié)構(gòu)組織

按照樹形結(jié)構(gòu)的方式組織軟件系統(tǒng),不同的大功能拆分為小功能,文件夾內(nèi)套文件夾的實(shí)現(xiàn)形式,命名上統(tǒng)一,方便按功能快速找到對(duì)應(yīng)的源碼。

2.2.6 降低模塊之間的耦合度

耦合性是一種軟件度量,是指一程序中模塊及模塊之間信息或參數(shù)依賴的程度;內(nèi)聚性是一個(gè)和耦合性相對(duì)的概念,一般而言低耦合性代表高內(nèi)聚性,反之亦然。

2.2.7 降低模塊與模塊之間通信

一個(gè)軟件內(nèi)模塊與模塊之間的通信,構(gòu)成了一個(gè)內(nèi)部的通信網(wǎng),避免內(nèi)部模塊的通信采用網(wǎng)狀結(jié)構(gòu),這種解決方案是設(shè)計(jì)模式中的中介者模式。

2.3 重構(gòu)和演進(jìn)架構(gòu)

架構(gòu)不能一成不變,要隨著業(yè)務(wù)需求的演進(jìn)而升級(jí)重構(gòu),一成不變的架構(gòu)是危險(xiǎn)的,總有一天架構(gòu)成為業(yè)務(wù)演進(jìn)的最嚴(yán)重的制約因素。

這種需要實(shí)際開發(fā)中除完成既定的項(xiàng)目外,預(yù)留部分人力進(jìn)行架構(gòu)升級(jí)維護(hù),持續(xù)小改動(dòng),不定期根據(jù)業(yè)務(wù)的需求進(jìn)行架構(gòu)的重構(gòu),未雨綢繆。

3、軟件問(wèn)題的分析與解決

嵌入式軟件由于調(diào)試手段的限制、部署場(chǎng)景的多樣化、軟硬件問(wèn)題混合在一起、外部環(huán)境因素的影響等因素,導(dǎo)致軟件經(jīng)常會(huì)遇到一些非常難以解決的問(wèn)題。

3.1 解題思想

熟悉軟件的業(yè)務(wù)流程:從業(yè)務(wù)的角度發(fā)現(xiàn)問(wèn)題、復(fù)現(xiàn)問(wèn)題并解決問(wèn)題。

熟悉軟件的總體架構(gòu):軟件架構(gòu)是解決難題問(wèn)題的基本框架,基于軟件架構(gòu)解決問(wèn)題不會(huì)陷入到局部細(xì)節(jié),導(dǎo)致修復(fù)一個(gè)問(wèn)題的同時(shí)產(chǎn)生新的問(wèn)題,不會(huì)犯原則性、方向性錯(cuò)誤。

熟悉軟件代碼的實(shí)現(xiàn):熟悉代碼的細(xì)節(jié),能夠更好、更快的在蛛絲馬跡中找到證據(jù)和突破點(diǎn),甚至在問(wèn)題還沒(méi)有收斂前,提供一種收斂的方向,引領(lǐng)問(wèn)題的解決,對(duì)代碼的熟悉程度直接關(guān)系到解決問(wèn)題的速度。

3.2 調(diào)試手段和信息不足相關(guān)問(wèn)題

3.2.1 現(xiàn)場(chǎng)偶發(fā)性、難復(fù)現(xiàn)性引發(fā)的問(wèn)題

一些偶發(fā)性現(xiàn)象級(jí)問(wèn)題,甚至導(dǎo)致系統(tǒng)偶發(fā)性的重啟,無(wú)法復(fù)現(xiàn),設(shè)備重啟之后,故障消失后,再也很難復(fù)現(xiàn)。

1、分析日志文件

從log中尋找異常提示,是應(yīng)對(duì)不可重復(fù)性、偶發(fā)性故障最基本的手段。在系統(tǒng)某處發(fā)生異常時(shí),一定會(huì)在log中留下蛛絲馬跡,可以請(qǐng)客戶協(xié)助提供串口日志,在log文件中查找問(wèn)題?;蛘咴O(shè)備自己內(nèi)部記錄log,但嵌入式設(shè)備由于存儲(chǔ)空間的限制,可能先前過(guò)于久遠(yuǎn)的信息,就會(huì)被新的信息被覆蓋,針對(duì)這種情況,就需要定期清除無(wú)效日志。有些異常會(huì)導(dǎo)致系統(tǒng)重啟,而重啟之后,就會(huì)導(dǎo)致異常信息被正常重啟的信息覆蓋,這就需要系統(tǒng)能夠支持log的備份。不管怎么樣,log為定位現(xiàn)場(chǎng)問(wèn)題提供了最基本的、最主要的信息來(lái)源。一個(gè)完善的log機(jī)制,對(duì)于定位現(xiàn)場(chǎng)問(wèn)題非常有幫助。如果不滿足,可能首要任務(wù)是先完善日志功能。

2、回退軟件版本,緊急消除現(xiàn)場(chǎng)問(wèn)題

有些現(xiàn)場(chǎng)問(wèn)題,雖然偶發(fā)事件,但發(fā)生后影響嚴(yán)重,客戶無(wú)法接受。針對(duì)這種情況,在解決問(wèn)題之前,可以先把軟件降級(jí),降級(jí)到相對(duì)穩(wěn)定,沒(méi)有嚴(yán)重故障的版本。

3、比較相鄰版本之間的代碼改動(dòng)

如果不容易復(fù)現(xiàn)的故障,確認(rèn)在升級(jí)了某個(gè)軟件版本之后才出現(xiàn)的,而其他現(xiàn)場(chǎng)條件都沒(méi)有變化,且分析log也無(wú)法發(fā)現(xiàn)異常點(diǎn)。此時(shí),一種高效的解決此問(wèn)題的方法,就是比較兩個(gè)版本之間的代碼的改動(dòng)。

代碼改動(dòng)比較少,分析代碼比較容易;如果代碼改動(dòng)比較多,就需要根據(jù)用戶描述的現(xiàn)象,結(jié)合前后代碼的改動(dòng)模塊,初步分析最可能是哪個(gè)模塊引起的,這種往往需要對(duì)系統(tǒng)架構(gòu)較深刻的理解。在眾多修改模塊中,分析最有可能關(guān)聯(lián)的代碼模塊的改動(dòng),然后逐一排查 。分析代碼的改動(dòng)與出現(xiàn)的現(xiàn)象之間可能的關(guān)聯(lián)關(guān)系,對(duì)開發(fā)人員個(gè)人的技術(shù)素養(yǎng)和方法論有較高的要求 。比較相鄰版本之間的代碼改動(dòng),針對(duì)某些棘手的現(xiàn)場(chǎng)問(wèn)題,有時(shí)候確實(shí)是一個(gè)非常有效的手段。

4、問(wèn)題復(fù)現(xiàn)

雖然常規(guī)來(lái)說(shuō)現(xiàn)場(chǎng)很難復(fù)現(xiàn),但可以人為的修改軟件、構(gòu)建或增加模擬數(shù)據(jù),人為創(chuàng)造或觸發(fā)條件,增加故障復(fù)現(xiàn)的幾率。在設(shè)計(jì)觸發(fā)條件時(shí),需要圍繞用戶描述的現(xiàn)場(chǎng)故障現(xiàn)象來(lái)設(shè)計(jì)觸發(fā)條件,觀察是否能否復(fù)現(xiàn),且表現(xiàn)一致。

5、分析代碼

根據(jù)用戶描述的現(xiàn)象,硬分析代碼,是一種通用的方法,放之四海皆準(zhǔn)的方法,熟悉自身代碼的邏輯關(guān)系是基本功,但解決問(wèn)題的效率就比較難把握了。

6、增加 log 更新版本繼續(xù)測(cè)

如果常規(guī)的log無(wú)法展現(xiàn)故障的異常,就需要在猜測(cè)有可能的部分增加日志,在現(xiàn)場(chǎng)復(fù)測(cè)。但這種日志添加的位置是否合理,決定了問(wèn)題再次出現(xiàn)時(shí)是否能定位問(wèn)題的準(zhǔn)確性。這種方法在工程實(shí)踐中,實(shí)施難度大,需要客戶多次配合。

3.2.2 現(xiàn)象與真正的原因不在一起的問(wèn)題

大多時(shí)候解決軟件故障,是可以做到頭痛醫(yī)頭,腳痛醫(yī)腳。有些時(shí)候,頭痛的原因并不在“頭”,而在“腳”。這就需要知道“頭痛” 與 “腳” 的某種關(guān)聯(lián)關(guān)系。

解決這樣的問(wèn)題,對(duì)技術(shù)人員的綜合技能的要求非常高,因?yàn)檫@個(gè)問(wèn)題,不再是局部問(wèn)題,而是發(fā)散到調(diào)查該問(wèn)題的技術(shù)人員不熟悉的其他的軟件組件領(lǐng)域。即使對(duì)于熟悉整個(gè)系統(tǒng)的人而言,也是一個(gè)難點(diǎn),因?yàn)閱?wèn)題的現(xiàn)象與根源之間的路徑是發(fā)散的,沒(méi)有一個(gè)確切的路徑。

首先,必須以故障的表面現(xiàn)象作為錨點(diǎn),作為出發(fā)點(diǎn)。為后續(xù)進(jìn)一步的調(diào)查立一個(gè)基點(diǎn)。根據(jù)現(xiàn)象找到出問(wèn)題的代碼,根據(jù)代碼和log分析代碼的表面原因。如果確實(shí)是本處代碼的問(wèn)題,直接在此解決即可。即頭痛醫(yī)頭,腳痛醫(yī)腳。

很多情形下,真正的原因不在顯示異常的地方,比如收到了異常的事件、或參數(shù)不合理、或自身狀態(tài)機(jī)的問(wèn)題等。這時(shí)候就需要追溯,為什么會(huì)有這樣的事件或消息?有時(shí)候,由于復(fù)雜系統(tǒng)的程序員沒(méi)有系統(tǒng)的視角,常以為消除了故障表面現(xiàn)象就是解決了問(wèn)題。很多時(shí)候站在系統(tǒng)的視角,可以從多個(gè)層面加以解決,消除異常事情,可以從規(guī)則過(guò)濾模塊解決,也可從前置模塊或后續(xù)模塊解決。具體在哪兒解決最合理,這就需要有系統(tǒng)和結(jié)構(gòu)的視角。當(dāng)然,也曾遇到有人解決類似問(wèn)題是屏蔽異常消息或者屏蔽ASSERT,并沒(méi)從根源去消除為什么產(chǎn)生了異常。

3.2.3 報(bào)錯(cuò)點(diǎn)發(fā)生在第三方庫(kù)內(nèi)部

軟件報(bào)錯(cuò)的地方是在第三方庫(kù),而第三方庫(kù)有沒(méi)有源代碼或不熟悉

如果集成的第三方庫(kù)沒(méi)有源代碼,則把這個(gè)問(wèn)題上報(bào)給第三方,讓第三方給出內(nèi)部出錯(cuò)的原因,更新庫(kù)或者配合抓日志分析。如果第三方庫(kù)有源代碼的話,可分析第三方代碼,增加日志或檢查傳入第三方庫(kù)函數(shù)的參數(shù)是否正確,是否合法;大多數(shù)時(shí)候,是錯(cuò)誤地傳入了不合適的參數(shù)給第三方庫(kù)。檢查使用第三方的時(shí)序是否正確,在軟件系統(tǒng)中,時(shí)序是一個(gè)非常重要,同樣的函數(shù),同樣的代碼,如果時(shí)序不對(duì),也會(huì)導(dǎo)致代碼邏輯紊亂。不過(guò)現(xiàn)在提供庫(kù)或者SDK,一般都有技術(shù)支持,也可直接尋求幫助。

3.2.4 軟硬件結(jié)合導(dǎo)致的無(wú)法定位的問(wèn)題

嵌入式系統(tǒng)中,有時(shí)候會(huì)出現(xiàn)硬件異常導(dǎo)致軟件狀態(tài)或邏輯錯(cuò)誤,硬件人員很難根據(jù)有限的信息判斷硬件到底怎么了,通常軟件和硬件就會(huì)反復(fù)的踢皮球。但是用戶角度看到的異常是在軟件這邊。

由于硬件團(tuán)隊(duì)對(duì)客戶現(xiàn)場(chǎng)的設(shè)備,通常沒(méi)有檢測(cè)手段來(lái)判斷是否真是硬件問(wèn)題的,軟件團(tuán)隊(duì)最好能夠通過(guò)日志配置,確認(rèn)硬件故障單元。或者直接將壞機(jī)寄回硬件部門,軟件配合復(fù)現(xiàn)問(wèn)題,以幫助硬件團(tuán)隊(duì)判斷。

硬件故障問(wèn)題,需要特別關(guān)注供電、時(shí)鐘信號(hào),復(fù)位時(shí)間等,曾經(jīng)遇到幾次因?yàn)榇诼╇姵鋈?dǎo)致外部傳感器復(fù)位異常的問(wèn)題??傊?,軟硬件的交合處,是容易扯皮的地方,這需要軟件人員也同時(shí)了解硬件的工作原理,在出故障時(shí),能夠更好的判斷是軟件異常,還是硬件真的有故障。

還有一個(gè)商業(yè)上的問(wèn)題,如果客戶感受到是硬件的問(wèn)題,需要回收設(shè)備,會(huì)造成很大的經(jīng)濟(jì)損失。一般情況下是軟件想辦法規(guī)避異常,畢竟軟件復(fù)制不需要成本。

3.3 內(nèi)存與指針相關(guān)問(wèn)題

3.3.1 隱性的內(nèi)存泄露問(wèn)題

內(nèi)存泄漏(Memory Leak)是指程序中已動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無(wú)法釋放,造成系統(tǒng)內(nèi)存的浪費(fèi),導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。內(nèi)存泄露是一個(gè)嚴(yán)重的慢性病,不會(huì)立即展現(xiàn),但不知道未來(lái)的哪一天,所有的設(shè)備,會(huì)在相近的時(shí)間點(diǎn)爆發(fā)問(wèn)題。

內(nèi)存泄漏還會(huì)導(dǎo)致系統(tǒng)意外的重啟,重啟的原因可能千奇百怪。因此,檢測(cè)和解決內(nèi)存泄漏,就顯得非常重要。

1. 泄漏的原因

內(nèi)存泄漏主要是發(fā)生在堆內(nèi)存分配方式中,即malloc方式中,申請(qǐng)的內(nèi)存沒(méi)有得到釋放,或者對(duì)應(yīng)的指針被被覆蓋,內(nèi)存直接泄漏。因?yàn)閮?nèi)存泄漏屬于程序運(yùn)行中的問(wèn)題,無(wú)法通過(guò)編譯識(shí)別,主要在程序運(yùn)行過(guò)程中來(lái)判別和診斷。

2. 動(dòng)態(tài)檢測(cè)或監(jiān)控是否內(nèi)存泄露

監(jiān)控系統(tǒng)內(nèi)存,周期性監(jiān)控堆中可用內(nèi)存的大小,是檢測(cè)系統(tǒng)是否有內(nèi)存泄漏的最有效的手段。系統(tǒng)的內(nèi)存短期會(huì)隨著業(yè)務(wù)數(shù)據(jù)的變化而變化,但長(zhǎng)期來(lái)看,可用的剩余可用內(nèi)存會(huì)圍繞一個(gè)中軸線上下波動(dòng),如果存在內(nèi)存泄漏,其剩余可用內(nèi)存隨隨著時(shí)間的推移逐漸減少。

3. 如何找到在哪兒內(nèi)存泄露

可以使用工具檢測(cè)代碼中有沒(méi)有靜態(tài)的內(nèi)存泄露,也可以在代碼中增加標(biāo)記,檢測(cè)長(zhǎng)期未釋放的堆是誰(shuí)申請(qǐng)的,在代碼中查找??梢詤⒖嘉恼隆?strong>動(dòng)態(tài)內(nèi)存管理及防御性編程》。

3.3.2 指針跑飛的問(wèn)題

指針跑飛就是指針指向不正確的位置,指針未初始化或數(shù)組/指針越界訪問(wèn),導(dǎo)致系統(tǒng)崩潰。

指針跑飛是常見的問(wèn)題,問(wèn)題很嚴(yán)重,但解決起來(lái)其實(shí)并不難,指針跑飛系統(tǒng)crash時(shí),如果平臺(tái)軟件會(huì)打印出函數(shù)調(diào)用棧、segment fault錯(cuò)誤、代碼出錯(cuò)的地方、coredump文件等信息。有了這些信息,再分析源代碼,其實(shí)是很容發(fā)現(xiàn)或找出當(dāng)前代碼中指針跑飛的原因的。

如果基于第三方的SDK開發(fā),指針跑飛直接就重啟,可能不會(huì)有任何提示,因此,最好能夠在編碼時(shí)就能夠提前預(yù)防,而不是等待程序跑飛之后再定位解決?。常見的手段:

1、熟悉和遵守代碼編寫規(guī)范,加強(qiáng)代碼的評(píng)審,把問(wèn)題消滅在編碼階段。

2、靜態(tài)檢測(cè)工具對(duì)代碼進(jìn)行檢測(cè)。

3、增加邊界性測(cè)試用例,一般指針異常是在邊界或異常情形下發(fā)生的。

4、增加異常場(chǎng)景的測(cè)試,異常場(chǎng)景是違反常規(guī)的測(cè)試場(chǎng)景,這些異常業(yè)務(wù)場(chǎng)景,能夠盡早shi發(fā)現(xiàn)隱藏的問(wèn)題。

3.3.3 空指針的問(wèn)題

空指針是“指針跑飛”的一種特殊情況,即指針為NULL,通常出現(xiàn)在指針用NULL值初始化后,在某些情況下沒(méi)有給指針賦值,就直接使用指針?lè)秶鷥?nèi)存?;蛘呓邮蘸瘮?shù)返回的指針變量,忽略了函數(shù)返回NULL的情形。

在使用指針前,檢查指針是否為空,如果為空,在代碼中執(zhí)行異常處理流程,如打印出錯(cuò)信息,或者ASSERT,這樣就可以避免引起更嚴(yán)重的問(wèn)題,相對(duì)來(lái)說(shuō)多使用一個(gè)if即可規(guī)避。

3.3.4 棧溢出導(dǎo)致的問(wèn)題

棧溢出時(shí)會(huì)訪問(wèn)不存在的RAM空間,造成代碼跑飛,這時(shí)無(wú)法得到溢出時(shí)的上下文數(shù)據(jù),也無(wú)法對(duì)后續(xù)的程序修改提供有用信息。

函數(shù)遞歸調(diào)用,系統(tǒng)要在棧中不斷保存函數(shù)調(diào)用時(shí)的現(xiàn)場(chǎng)和產(chǎn)生的變量,如果遞歸調(diào)用太深,就會(huì)造成棧溢出。函數(shù)內(nèi)局部數(shù)組變量的內(nèi)存空間過(guò)大,或者局部數(shù)組變量的下標(biāo)范圍溢出,破壞了棧空間中的內(nèi)容。這種問(wèn)題容易解決但初始不容易查到原因。如果是帶操作系統(tǒng)的,一般系統(tǒng)內(nèi)核會(huì)直接提示??臻g不足,將任務(wù)棧空間加大,或者不靜態(tài)分配,用malloc動(dòng)態(tài)創(chuàng)建,從堆中分配的。平時(shí)編碼中禁止使用循環(huán)遞歸函數(shù)。

3.4 軟件時(shí)序設(shè)計(jì)相關(guān)的問(wèn)題

時(shí)序問(wèn)題是最容易出問(wèn)題的地方,“時(shí)”代表時(shí)間順序和時(shí)效性,一旦執(zhí)行順序錯(cuò)亂,或執(zhí)行過(guò)慢失去時(shí)效,就會(huì)導(dǎo)致錯(cuò)誤。

3.4.1 消息的串行化處理

每個(gè)任務(wù)、線程,只能按順序的處理串行的消息,然而,其他線程發(fā)送過(guò)來(lái)的消息并不是串行發(fā)送的,不同線程都是并行、異步發(fā)送消息的,這會(huì)導(dǎo)致線程在沒(méi)有處理完一個(gè)消息,另一個(gè)消息又回來(lái)了。如何把外部的并發(fā)消息轉(zhuǎn)換成線程的串行處理呢?

每個(gè)任務(wù)、線程都應(yīng)有一個(gè)消息隊(duì)列,外部線程向消息隊(duì)列中發(fā)送數(shù)據(jù),目標(biāo)線程從消息隊(duì)列中讀取消息,這樣所有的消息被串行在消息隊(duì)列中,線程就會(huì)串行的處理每個(gè)消息,只有當(dāng)一個(gè)消息處理完(函數(shù)調(diào)用返回)時(shí),才會(huì)處理另一個(gè)消息。參考《嵌入式軟件的設(shè)計(jì)模式(上)》中的 第3.3節(jié)?“隊(duì)列模式”。

3.4.2 超時(shí)或消息丟失引發(fā)的問(wèn)題

一個(gè)任務(wù)、線程給另一個(gè)任務(wù)、線程發(fā)送消息,等待對(duì)方的應(yīng)答,有時(shí)候?qū)Ψ矫?,發(fā)送時(shí)隊(duì)列滿發(fā)送失敗,或者接收方?jīng)]有處理回復(fù),等待一段時(shí)間后空閑了才處理該消息并應(yīng)答時(shí),但對(duì)于發(fā)送方已經(jīng)超時(shí)。發(fā)送方超時(shí),就需要進(jìn)入異常處理。這里容易出問(wèn)題,它可能會(huì)引發(fā)一連串的異常處理反應(yīng),也有可能影響后續(xù)的正常消息的處理。

消息丟失是必須考慮情況,發(fā)送方不能假設(shè)接收方一定能夠收到消息,也不能假設(shè)接收方一定能夠及時(shí)的回應(yīng),必須充分考慮到消息因?yàn)閭鬏數(shù)膯?wèn)題丟失或?qū)Ψ矫Γ瑳](méi)有及時(shí)回應(yīng)的情形。

消息丟失就容易產(chǎn)生理論上該執(zhí)行的動(dòng)作沒(méi)有執(zhí)行,或者消息里面動(dòng)態(tài)內(nèi)存未釋放?;蛘呦⑻幚砺龑?dǎo)致對(duì)外設(shè)的控制延遲產(chǎn)生異常,曾經(jīng)出現(xiàn)共享單車鎖里面的馬達(dá)停止消息處理不及時(shí)導(dǎo)致車鎖無(wú)法再次上鎖。尤其處理通信時(shí)序要求嚴(yán)格,或外設(shè)控制要及時(shí)的場(chǎng)景需要注意。

3.4.3 性能本身問(wèn)題

數(shù)據(jù)處理尤其是復(fù)雜算法耗時(shí),導(dǎo)致消息處理不及時(shí),最終對(duì)外設(shè)的控制或者通信交互時(shí)序狀態(tài)延遲,產(chǎn)生異常。這種只能優(yōu)化算法,或?qū)r(shí)序部分單獨(dú)特殊處理,不考慮設(shè)計(jì)模式保執(zhí)行效率。或者評(píng)估階段就選擇性能資源更佳的硬件方案。

3.5 異常處理不充分問(wèn)題

軟件設(shè)計(jì)一般是考慮正常流程,然而實(shí)際運(yùn)行中,并非是理想狀態(tài),系統(tǒng)總會(huì)遇到各種異常,健壯的系統(tǒng),能夠充分考慮到各種異常情況,一旦異常發(fā)生,程序也不會(huì)輕易崩潰。

超時(shí):增加超時(shí)定時(shí)器事件以及事件處理,不能假設(shè)對(duì)方一定應(yīng)答消息。

空指針:不能假設(shè)一定能夠申請(qǐng)到內(nèi)存,要考慮到返回為NULL的情形,通過(guò)指針訪問(wèn)內(nèi)存對(duì)象時(shí)需要及時(shí)的檢查指針是否為空。

并發(fā)訪問(wèn):在并發(fā)執(zhí)行的系統(tǒng)中,如果要訪問(wèn)全局變量,不能假設(shè)只有一個(gè)線程訪問(wèn)全局變量,需要通過(guò)鎖對(duì)全局共享資源進(jìn)行加鎖,特別是要訪問(wèn)全局的數(shù)據(jù)結(jié)構(gòu)。

消息隊(duì)列:不能假設(shè)消息隊(duì)列始終有效,要考慮消息隊(duì)列滿或空的情形。

設(shè)計(jì):在軟件設(shè)計(jì)時(shí)就考慮軟件的異常處理機(jī)制,功能層面就支持異常記錄、售后調(diào)試的需求,而不是把這個(gè)工作留給編程人員。

4、非技術(shù)性問(wèn)題

大規(guī)模系統(tǒng)中軟件會(huì)分割成無(wú)數(shù)個(gè)模塊,負(fù)責(zé)這些的開發(fā)人員來(lái)自不同的職能部門,這些部門又有著各自不同優(yōu)先級(jí)的任務(wù),而解決那些復(fù)雜的問(wèn)題又需要這些部門的配合,才能找到真正的出錯(cuò)原因和找到最終的解決方案?;蛟S最終解決問(wèn)題的代碼僅僅在一個(gè)組件中,或許只有簡(jiǎn)單的一兩行代碼,然而找到問(wèn)題的原因并給定解決方案的過(guò)程卻是漫長(zhǎng)、繁瑣的。

4.1 人員分配

在大規(guī)模分工的系統(tǒng)中,每個(gè)技術(shù)領(lǐng)域都是一群人負(fù)責(zé),解決問(wèn)題效率與個(gè)人的技術(shù)能力有極大的關(guān)系。有些簡(jiǎn)單技術(shù)問(wèn)題,如果參與的不是合適的技術(shù)人員,例如人員的變動(dòng)導(dǎo)致原來(lái)的軟件人員不再負(fù)責(zé)該軟件,那簡(jiǎn)單的問(wèn)題也變得復(fù)雜。正所謂“難者不會(huì),會(huì)者不難”。技術(shù)是長(zhǎng)期積累的過(guò)程,同一類問(wèn)題不要頻繁的更換人員。

4.2 任務(wù)分配

復(fù)雜系統(tǒng)中,一個(gè)問(wèn)題需要涉及到多個(gè)部門,一個(gè)程序員在某個(gè)環(huán)節(jié)上的放緩,導(dǎo)致基于這個(gè)前置條件的進(jìn)度也跟著放緩,整個(gè)組織的執(zhí)行就會(huì)極大的降低,從宏觀上看,整個(gè)組織都很忙,但總體的效率卻很低。

按優(yōu)先級(jí)排序分配任務(wù),在一個(gè)高優(yōu)先任務(wù)完成前,不要處理其他任務(wù),降低程序員并發(fā)處理故障的個(gè)數(shù),否則“任務(wù)”切換的開銷就很大。高難度與低難度任務(wù)搭配,同時(shí)處理多個(gè)高難度問(wèn)題,整體效率是下降的,而且一個(gè)延期會(huì)連鎖導(dǎo)致后續(xù)都延期。

4.3 項(xiàng)目分配

實(shí)現(xiàn)一個(gè)復(fù)雜功能,或者解決某個(gè)問(wèn)題,往往可以在不同的子系統(tǒng)解決,各個(gè)子系統(tǒng)出于各自原因,都希望最終的解決方案不要放到自己的子系統(tǒng),希望推到其他子系統(tǒng)。此時(shí),一般是地位低的子系統(tǒng)人員妥協(xié),即使其子系統(tǒng)的實(shí)現(xiàn)不是最佳方案,這種就看項(xiàng)目分配,需要具有一定技術(shù)背景和決策權(quán)的人協(xié)調(diào)。

5、結(jié)語(yǔ)

關(guān)于軟件問(wèn)題的分析理論,也只是拾人牙慧。理論和實(shí)際存在較大差異,真正遇到軟件問(wèn)題,這些方法并不一定靠譜。

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
AFBR-79EQDZ 1 Foxconn Transceiver, 840nm Min, 860nm Max, MTP Connector, Panel Mount, ROHS COMPLIANT, PACKAGE-38
$113.6 查看
CSTNE10M0G550000R0 1 Murata Manufacturing Co Ltd Ceramic Resonator,

ECAD模型

下載ECAD模型
$0.65 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

嵌入式系統(tǒng)開發(fā)技術(shù)交流,軟件開發(fā)的思路與方案共享,行業(yè)資訊的分享。