嵌入式軟件設(shè)計中查找缺陷的幾個技巧
清單1:競爭條件
如果在數(shù)據(jù)寫入之前,使用shared_sensor的另一個線程或ISR先占(preempt)了這個線程,它將得到原來的傳感器讀數(shù)。使用臨時副本可以防止先占線程讀取只經(jīng)過部分處理的數(shù)據(jù)。不過,如果這些代碼在一個數(shù)據(jù)總線不足32位的處理器上運行,就會存在競爭條件。
在一個8位或16位的處理器上,向shared_sensor的寫入操作并不是一次性完成的。在8位處理器上,寫入32位浮點值可能需要四條指令,在16位處理器上可能需要兩條指令。如果在對shared_sensor進行連續(xù)寫入中途Update_Sensor()被先占,則先占線程將從由一部分老數(shù)據(jù)和一部分新數(shù)據(jù)組成的shared_sensor讀取一個數(shù)值。根據(jù)應(yīng)用的具體情況,這有可能造成嚴重的后果。解決的辦法是鎖定調(diào)度程序,或在更新共享變量期間禁止中斷。
消除競爭條件通常很簡單,但找出隱藏在代碼中的競爭條件則需要仔細的分析。
對于由一個循環(huán)程序和不同ISR組成的簡單系統(tǒng),分析競爭條件很簡單,只需檢查每個ISR并識別它引用的所有共享變量。共享變量通常是這些系統(tǒng)中的全局數(shù)據(jù),一旦這些共享變量被找出來之后,就可以檢查它們在代碼中的各次使用情況。每次訪問都必須按需要進行保護,以避免潛在的沖突。在簡單設(shè)計中,一般通過在關(guān)鍵代碼段周圍禁止中斷來實現(xiàn)保護。遵守下列規(guī)則可幫助避免競爭問題:
* 如果一個ISR對共享數(shù)據(jù)進行寫入,則該ISR之外的每次可中斷的讀操作都必須予以保護。
* 如果一個ISR對共享數(shù)據(jù)進行寫入,則該ISR之外的任何讀-修-寫操作都必須予以保護。
* 如果一個ISR讀取共享數(shù)據(jù),則對該數(shù)據(jù)的可中斷寫操作必須予以保護。
* 如果一個ISR和其它代碼都要檢查一個硬件狀態(tài)標(biāo)志,以便在使用某資源之前確定其可用性,如:
if (!resource_busy)
{
// Use resource
}
則從檢查標(biāo)志之時開始,到硬件設(shè)置標(biāo)志表示資源不可用為止,必須采取保護措施。
對于使用了優(yōu)先級不同的多個線程的更為復(fù)雜的系統(tǒng),其分析也非常相似。上述規(guī)則仍然適用于ISR使用的所有數(shù)據(jù)。此外,還必須識別出每個線程使用的共享數(shù)據(jù)。首先從系統(tǒng)中優(yōu)先級最高的線程開始,找出它與任何優(yōu)先級較低的線程共享的所有數(shù)據(jù),然后按照上述四條規(guī)則進行保護。對于軟件使用的其它每個優(yōu)先級,再重復(fù)這一過程。
注意,如果系統(tǒng)采用了一種循環(huán)調(diào)度算法,則特定優(yōu)先級內(nèi)的所有線程可在任意時刻相互先占。這意味著前述四條分析規(guī)則在考慮較低優(yōu)先級的線程之外,還必須考慮同一優(yōu)先級的所有線程。
多線程系統(tǒng)通常使用某種類型的操作系統(tǒng),它能夠提供多種保護選擇??梢允褂没コ饣蛐盘柫?,或者鎖定調(diào)度器。有時也可使用其它進程間通信(IPC)基本技術(shù):通過向消息隊列發(fā)送消息(而非修改共享變量)來表示數(shù)據(jù)已經(jīng)改變。在許多情況下,最好由單一線程來管理共享資源,它負責(zé)處理所有的讀寫請求,并在內(nèi)部防止訪問沖突。
評論