嵌入式軟件設計中查找缺陷的幾個(gè)技巧
1. 在測試期間,觀(guān)察堆棧所能達到的深度,并保證有較大的堆??臻g余量。
2. 檢測堆棧溢出,并采取改進(jìn)措施。
觀(guān)察堆棧深度的方法很簡(jiǎn)單:
* 向整個(gè)內存堆棧區寫(xiě)入一個(gè)特定的數據圖案符號,如55AA。
* 在預期使用最大堆??臻g的條件下運行系統。
* 使用仿真器或其它工具檢查堆棧存儲區,看有多少符號圖案由于堆棧的使用而被改寫(xiě)了。
當然,這些步驟并不能保證在一些不同條件下不會(huì )需要更多的堆棧,但確實(shí)可以表明所需要的最小堆棧數。
使用帶內存管理單元(MMU)的處理器時(shí),有可能檢測出運行時(shí)的堆棧溢出現象。MMU將內存劃分為多個(gè)區域,用一個(gè)受保護的內存段來(lái)“警戒”堆棧區域。發(fā)生堆棧溢出時(shí),處理器將訪(fǎng)問(wèn)這個(gè)受保護段。這個(gè)操作將引發(fā)一個(gè)異常事件(如產(chǎn)生SIGSEGV信號),可被程序捕獲到。創(chuàng )建線(xiàn)程時(shí),與實(shí)時(shí)POSIX標準兼容的RTOS提供有這種堆棧警戒功能選項,大大簡(jiǎn)化了編程人員的工作。GNU工具等其它開(kāi)發(fā)環(huán)境包含有編譯器開(kāi)關(guān),可在程序中添加實(shí)現堆棧警戒功能所需的代碼,但它們仍然依靠底層操作系統來(lái)有效地處理堆棧溢出。但是,按照這種方式檢測溢出還只是問(wèn)題的一部分。為了使這類(lèi)設計更為有效,系統必須能夠從堆棧溢出中恢復過(guò)來(lái)并繼續正確地工作。
在一個(gè)對安全或任務(wù)要求嚴格的應用中,系統運行時(shí)在測試或檢測堆棧溢出期間監視堆棧的深度可能并不是一項足夠的風(fēng)險控制措施。對于一些應用,必須確保系統絕對不會(huì )越出所分配的堆棧范圍;只有通過(guò)完整的堆棧深度分析才能證明這一點(diǎn)。這意味著(zhù),如果整個(gè)程序在同一內存空間運行,則必須對所有代碼執行這項分析。不過(guò),如果使用MMU,分析??珊?jiǎn)化。在設計系統時(shí),可將所有關(guān)鍵代碼置于一個(gè)或多個(gè)獨立線(xiàn)程內,而這些線(xiàn)程分別在各自的保護內存段中運行。這樣,只要對這些關(guān)鍵線(xiàn)程進(jìn)行堆棧使用分析就可以了。當然,這項簡(jiǎn)化設計假定當非關(guān)鍵線(xiàn)程溢出其堆棧并失效時(shí),關(guān)鍵線(xiàn)程仍可正確執行。
由于分析工作所需的堆棧使用數據來(lái)自匯編語(yǔ)言清單,因此修改代碼時(shí),相應模塊的堆棧使用信息必須予以更新。如果使用不同的編譯器版本,或者改變了優(yōu)化設置,也必須復核整個(gè)分析過(guò)程。在理想情況下,編譯器將提供每個(gè)函數(如果不是每個(gè)線(xiàn)程的話(huà))的堆棧使用數量,因為它擁有計算需要的所有信息。例如,瑞薩公司提供有Call Walker,這是該公司高性能的Embedded Workshop開(kāi)發(fā)環(huán)境的一部分。這個(gè)工具可以圖形化地顯示每個(gè)函數使用的調用樹(shù)和堆棧,包括運行時(shí)庫和C庫的函數。Call Walker也能找出使用堆棧數量最大的路徑。使用這樣的工具可以實(shí)現步驟1到步驟3的自動(dòng)化。
大部分軟件開(kāi)發(fā)項目依靠結合代碼檢查、結構測試和功能測試來(lái)識別軟件缺陷。盡管這些傳統技術(shù)非常重要,而且能發(fā)現大多數軟件問(wèn)題,但它們無(wú)法檢查出當今復雜系統中的許多共性錯誤。本文將介紹如何避免那些隱蔽然而常見(jiàn)的錯誤,并介紹的幾個(gè)技巧幫助工程師發(fā)現軟件中隱藏的錯誤。
二、競爭條件
當兩個(gè)或更多獨立線(xiàn)程同時(shí)訪(fǎng)問(wèn)同一資源時(shí),就出現了競爭條件。競爭條件的影響多種多樣,取決于具體的情況。清單1解釋了一個(gè)潛在的競爭條件。函數Update_Sensor()通過(guò)調用get_raw()來(lái)讀取傳感器的原始數據。在處理過(guò)程中,該數據被乘上一個(gè)定標因子,并加上一個(gè)偏移量。處理是在該數據的一個(gè)臨時(shí)副本上進(jìn)行的,然后,該臨時(shí)副本被寫(xiě)入共享變量。
評論