張睿 周波 郝維寧 李露銘 喬梁
(北京空間飛行器總體設(shè)計(jì)部,北京 100094)
星載軟件是運(yùn)行在衛(wèi)星上的嵌入式軟件,典型的星載軟件有數(shù)管中心計(jì)算機(jī)軟件和控制應(yīng)用軟件。數(shù)管中心計(jì)算機(jī)軟件主要負(fù)責(zé)整個(gè)衛(wèi)星的數(shù)據(jù)業(yè)務(wù)[1],對(duì)衛(wèi)星提供遙測(cè)、遙控業(yè)務(wù)??刂茟?yīng)用軟件主要對(duì)衛(wèi)星進(jìn)行姿態(tài)軌道控制操作。上述軟件按照關(guān)鍵等級(jí)劃分都屬于A類(lèi)軟件,其功能對(duì)衛(wèi)星至關(guān)重要。堆棧是軟件運(yùn)行過(guò)程中最常用的存儲(chǔ)空間資源之一,堆棧中不僅存放了調(diào)用函數(shù)傳遞的參數(shù)及返回地址,還存放了函數(shù)的局部變量。但是,在軟件開(kāi)發(fā)的過(guò)程中,對(duì)于堆棧的分配及實(shí)際使用情況并不能獲得直觀的數(shù)據(jù)進(jìn)行比對(duì)驗(yàn)證。堆棧分配過(guò)多,會(huì)造成星載計(jì)算機(jī)資源嚴(yán)重浪費(fèi);分配不足,有可能產(chǎn)生堆棧溢出,造成軟件癱瘓。
對(duì)于堆棧深度的檢測(cè),通常分靜態(tài)測(cè)試方法[2]和動(dòng)態(tài)測(cè)試方法。靜態(tài)測(cè)試方法一般由專(zhuān)用工具提供支持,例如由AbsInt公司開(kāi)發(fā)Stack Analyzer堆棧分析工具。該工具沒(méi)有得到廣泛使用,一是由于價(jià)格較為昂貴,二是分析過(guò)程對(duì)函數(shù)指針、遞歸邏輯分析等支持不太到位。動(dòng)態(tài)測(cè)試方法即在系統(tǒng)運(yùn)行過(guò)程中對(duì)軟件使用的堆棧情況進(jìn)行測(cè)試,如上海創(chuàng)景公司的RTInsight工具在和目標(biāo)系統(tǒng)接口匹配后可用于堆棧的動(dòng)態(tài)測(cè)試。另外,也有人通過(guò)在系統(tǒng)中設(shè)計(jì)了專(zhuān)門(mén)的測(cè)試線(xiàn)程對(duì)堆棧進(jìn)行周期性檢查,通過(guò)比對(duì)特征字的方式獲取當(dāng)前堆棧的使用情況,起到了一定的效果[3]。動(dòng)態(tài)測(cè)試方法對(duì)測(cè)試用例的設(shè)計(jì)要求較高,如果不能分析出堆棧最深的函數(shù)調(diào)用路徑,即便程序運(yùn)行相當(dāng)長(zhǎng)的時(shí)間,仍然無(wú)法獲得堆棧最大使用深度,不能確保后續(xù)使用過(guò)程中不發(fā)生堆棧溢出的問(wèn)題。對(duì)于堆棧溢出的防護(hù)技術(shù)應(yīng)用比較廣泛的有StackGuard和StackSheild[4],其原理是對(duì)每個(gè)函數(shù)都加入部分用于調(diào)用時(shí)創(chuàng)建“哨兵”和返回時(shí)進(jìn)行比對(duì)的代碼,由于每個(gè)函數(shù)調(diào)用和返回都要運(yùn)行這些代碼,因此軟件運(yùn)行的效率受到比較大的影響,難以滿(mǎn)足星載軟件的實(shí)時(shí)性要求。
綜上所述,星載軟件開(kāi)發(fā)過(guò)程中堆棧使用存在如下問(wèn)題。①設(shè)計(jì)師無(wú)法掌握堆棧實(shí)際使用和余量的情況,憑經(jīng)驗(yàn)開(kāi)辟堆??臻g,普遍存在余量不足或浪費(fèi)的現(xiàn)象。②堆棧深度檢測(cè)困難,靜態(tài)測(cè)試比較依賴(lài)工具,動(dòng)態(tài)測(cè)試比較依賴(lài)用例設(shè)計(jì),效果都難以令人滿(mǎn)意。③軟件在堆棧溢出前不能提前進(jìn)行預(yù)防,無(wú)法提供相應(yīng)的報(bào)警或保護(hù)措施。④軟件在堆棧溢出后,軟件崩潰時(shí)的程序運(yùn)行位置一般都不在產(chǎn)生溢出的代碼附近,故障的滯后特性使得問(wèn)題很難排查[5-6]。⑤軟件崩潰后的行為具有不可預(yù)測(cè)性,存在引起二次故障的風(fēng)險(xiǎn)。另外,也很難設(shè)計(jì)針對(duì)性的防護(hù)措施。本文針對(duì)上述問(wèn)題,提出了一種應(yīng)用EDAC容錯(cuò)技術(shù)的堆棧溢出實(shí)時(shí)檢測(cè)方法,以BM3803處理器為例,介紹其EDAC容錯(cuò)機(jī)制,通過(guò)EDAC容錯(cuò)技術(shù),使星載軟件具備動(dòng)態(tài)、實(shí)時(shí)檢測(cè)堆棧深度和堆棧溢出的能力。
星載軟件在運(yùn)行時(shí),其程序和數(shù)據(jù)在內(nèi)存中被分為代碼段(Text Section)、數(shù)據(jù)段等幾個(gè)部分,其中,數(shù)據(jù)段又包括有初值的數(shù)據(jù)段(Data Section)和無(wú)初值的數(shù)據(jù)段(Bss Section)。內(nèi)存中剩余的空間被分配給堆空間和??臻g,一般由操作系統(tǒng)對(duì)它們進(jìn)行統(tǒng)一分配和管理。圖1為一個(gè)星載軟件內(nèi)存分配的示意。
圖1 星載軟件內(nèi)存分配Fig.1 Memory map of on-board software
BM3803是采用SPARC架構(gòu)的處理器[7],由于其具備錯(cuò)誤監(jiān)測(cè)與糾正(EDAC)保護(hù)機(jī)制,對(duì)存儲(chǔ)空間具備“糾一檢二”的能力,因此在航天領(lǐng)域廣泛使用。該處理器具有8組窗口寄存器,每組窗口寄存器由8個(gè)輸入寄存器、8個(gè)本地寄存器和8個(gè)輸出寄存器組成,輸入、輸出寄存器主要用于向函數(shù)傳遞參數(shù)、接收函數(shù)的返回值。第6個(gè)輸出寄存器保存棧指針(sp),指向當(dāng)前堆棧的棧頂,不再用于保存程序傳遞參數(shù)。因此,輸出寄存器最多可以傳遞6個(gè)參數(shù),更多參數(shù)通過(guò)堆棧傳遞。第6個(gè)輸入寄存器保存堆指針(fp),指向當(dāng)前堆棧的棧底,不再用于保存程序傳遞參數(shù)。程序堆棧由堆指針和棧指針組成,堆棧由高地址向低地址方向生長(zhǎng)。每個(gè)函數(shù)所占用的堆棧稱(chēng)為一個(gè)棧幀。如函數(shù)A調(diào)用函數(shù)B,函數(shù)B又調(diào)用函數(shù)C,其堆棧的示意如圖2所示。
在操作系統(tǒng)軟件的支持下,星載軟件一般都由幾個(gè)線(xiàn)程承載其功能,每個(gè)線(xiàn)程的堆棧都從圖1中的棧空間中分配獲得。線(xiàn)程的堆棧發(fā)生溢出,有可能更改了其他線(xiàn)程的堆棧,造成其他線(xiàn)程啟動(dòng)運(yùn)行時(shí)發(fā)生錯(cuò)誤。
圖2 BM3803處理器堆棧示意Fig.2 Stack of BM3803 processor
BM3803處理器對(duì)外部存儲(chǔ)器訪(fǎng)問(wèn)具有EDAC糾錯(cuò)檢錯(cuò)功能,其外部存儲(chǔ)器寬度一般為40 bit,其中32 bit存放程序指令或數(shù)據(jù),8 bit存放指令或數(shù)據(jù)對(duì)應(yīng)的EDAC校驗(yàn)(BM3803僅產(chǎn)生7 bit校驗(yàn)),其校驗(yàn)生成算法參見(jiàn)文獻(xiàn)[7]。當(dāng)處理器EDAC功能處于使能狀態(tài),讀取存儲(chǔ)器中的數(shù)據(jù)時(shí),能夠糾正32 bit數(shù)據(jù)和7 bit校驗(yàn)中的1 bit錯(cuò)誤,并能夠檢測(cè)2 bit錯(cuò)誤。本文利用上述保護(hù)機(jī)制中的檢錯(cuò)部分,處理器對(duì)某地址進(jìn)行字節(jié)寫(xiě)入或半字寫(xiě)入操作時(shí),首先需要將包含該字節(jié)或半字的32 bit數(shù)據(jù)和對(duì)應(yīng)的7 bit校驗(yàn)讀回,將待寫(xiě)入的字節(jié)或半字替換32 bit數(shù)據(jù)中的相應(yīng)內(nèi)容,并重新生成7 bit校驗(yàn),最終寫(xiě)回該地址。在讀取原始數(shù)據(jù)的過(guò)程中,如果該地址存儲(chǔ)的40 bit數(shù)據(jù)存在2 bit錯(cuò)誤,則處理器觸發(fā)0x2B陷阱。
正常情況下,處理器對(duì)外部存儲(chǔ)器進(jìn)行寫(xiě)操作時(shí),處理器對(duì)待寫(xiě)入的數(shù)據(jù)進(jìn)行EDAC計(jì)算,得到校驗(yàn),然后將數(shù)據(jù)連同校驗(yàn)一起寫(xiě)入存儲(chǔ)器,此時(shí),數(shù)據(jù)和校驗(yàn)是一一對(duì)應(yīng)的。本文方法中,需要利用處理器造錯(cuò)機(jī)制,使寫(xiě)入的數(shù)據(jù)和校驗(yàn)非一一對(duì)應(yīng),即寫(xiě)入的40 bit數(shù)據(jù)存在2 bit錯(cuò)誤。
造錯(cuò)機(jī)制需要設(shè)置BM3803處理器的存儲(chǔ)器容錯(cuò)配置寄存器1、2、3,具體操作如下。
(1)設(shè)置容錯(cuò)配置寄存器1的寫(xiě)旁路(EWB)字段,第22位,第20位,第18位分別為0,1,1,使能寫(xiě)旁路(即對(duì)存儲(chǔ)器進(jìn)行造錯(cuò))。
(2)設(shè)置容錯(cuò)配置寄存器2(32 bit數(shù)據(jù)位對(duì)應(yīng)的翻轉(zhuǎn)位)、容錯(cuò)寄存器3(7 bit校驗(yàn)位對(duì)應(yīng)的翻轉(zhuǎn)位)對(duì)需要造錯(cuò)的數(shù)據(jù)位或校驗(yàn)位進(jìn)行設(shè)置。
(3)操作外部存儲(chǔ)器地址,寫(xiě)入32 bit數(shù)據(jù),此時(shí)待寫(xiě)入的32 bit數(shù)據(jù)同容錯(cuò)寄存器2中的32 bit翻轉(zhuǎn)位進(jìn)行異或操作,校驗(yàn)位同容錯(cuò)寄存器3中的低7 bit翻轉(zhuǎn)位進(jìn)行異或操作。處理器把經(jīng)過(guò)異或的數(shù)據(jù)位和校驗(yàn)位寫(xiě)入外部存儲(chǔ)器。
造錯(cuò)機(jī)制的示意如圖3所示。
圖3 BM3803處理器造錯(cuò)機(jī)制示意Fig.3 Error making mechanism of BM3803 processor
本文針對(duì)上述問(wèn)題,提出一種應(yīng)用EDAC容錯(cuò)技術(shù)的星載軟件堆棧溢出實(shí)時(shí)檢測(cè)方法,以采用BM3803處理器的星載軟件為例,其檢測(cè)原理為:運(yùn)用BM3803處理器對(duì)隨機(jī)存儲(chǔ)器(RAM)空間的EDAC保護(hù)和造錯(cuò)機(jī)制,通過(guò)造錯(cuò)機(jī)制在堆??臻g中設(shè)置雷區(qū)和隔離區(qū)(雷區(qū)和隔離區(qū)僅僅為邏輯區(qū)分,實(shí)際功能和設(shè)置均相同,具體參見(jiàn)第2.2節(jié)),并將雷區(qū)和隔離區(qū)按照4字節(jié)地址對(duì)齊進(jìn)行初始化,使每個(gè)32 bit數(shù)據(jù)均存在雙比特錯(cuò)誤。在軟件運(yùn)行過(guò)程中,隨著棧區(qū)的生長(zhǎng),當(dāng)堆棧超過(guò)初始允許范圍并嘗試對(duì)雷區(qū)進(jìn)行字節(jié)或半字寫(xiě)操作時(shí),BM3803處理器會(huì)立即產(chǎn)生EDAC錯(cuò)誤陷阱(陷阱號(hào)0x2B)。軟件通過(guò)掛接相應(yīng)的陷阱處理程序,獲得陷阱出錯(cuò)地址,對(duì)相應(yīng)雷區(qū)進(jìn)行清掃,滿(mǎn)足堆棧生長(zhǎng),使軟件可以繼續(xù)正常運(yùn)行,并根據(jù)清掃的雷區(qū)計(jì)算當(dāng)前堆棧的使用深度。如果堆棧一直生長(zhǎng),直至觸碰隔離區(qū),嘗試對(duì)隔離區(qū)進(jìn)行字節(jié)或半字寫(xiě)操作,軟件進(jìn)入陷阱處理程序后可以通過(guò)陷阱出錯(cuò)地址分析得出該線(xiàn)程即將發(fā)生堆棧溢出,并針對(duì)堆棧溢出設(shè)計(jì)有針對(duì)性的恢復(fù)措施,如主動(dòng)復(fù)位、切換至備份計(jì)算機(jī)工作等。本文方法中,核心的2個(gè)過(guò)程是堆棧初始化和陷阱處理(見(jiàn)圖4)。
圖4 堆棧初始化和陷阱處理流程Fig.4 Flow of stack initialization and trap processing
需要說(shuō)明的是,本文方法觸發(fā)陷阱的條件是對(duì)雷區(qū)和隔離區(qū)進(jìn)行字節(jié)或半字寫(xiě)操作,對(duì)應(yīng)于程序函數(shù)中需要存在局部變量,且數(shù)據(jù)類(lèi)型為8 bit或16 bit,程序進(jìn)入雷區(qū)后對(duì)雷區(qū)中存在雙比特錯(cuò)誤的地址進(jìn)行讀操作,會(huì)引發(fā)0x9號(hào)陷阱,由于C語(yǔ)言編程規(guī)范[8]要求軟件編碼時(shí)對(duì)所有使用變量進(jìn)行初始化操作,軟件不應(yīng)存在對(duì)局部變量未初始化就進(jìn)行讀取使用的操作,因此不會(huì)產(chǎn)生0x9號(hào)陷阱。同樣的,規(guī)范中要求對(duì)局部變量進(jìn)行初始化,對(duì)應(yīng)于程序函數(shù)中定義的所有局部變量(包括8 bit或16 bit變量),都需要進(jìn)行賦值寫(xiě)操作。對(duì)于衛(wèi)星數(shù)管軟件,從功能上分析,大多圍繞遙測(cè)、遙控開(kāi)展,這些功能包括大量協(xié)議格式操作和數(shù)據(jù)轉(zhuǎn)換操作,對(duì)應(yīng)于軟件實(shí)現(xiàn)過(guò)程存在大量的8 bit和16 bit操作,另外,其底層硬件接口,包括串口和1553B總線(xiàn)等,也均為8 bit或16 bit端口,因此,程序在實(shí)現(xiàn)過(guò)程中,大多數(shù)函數(shù)均需要定義8 bit或16 bit局部變量進(jìn)行數(shù)據(jù)交互操作。綜上所述,本文方法非常適用于此類(lèi)軟件。如果軟件中絕大多數(shù)變量定義是32 bit或64 bit的,則需要在函數(shù)中顯示增加哨兵,即定義一個(gè)8 bit或16 bit局部變量并進(jìn)行初始化,用于提高程序在雷區(qū)或隔離區(qū)觸發(fā)陷阱的概率。
圖5給出了一個(gè)使用本文方法的線(xiàn)程堆棧生長(zhǎng)實(shí)例。
注:N為初始雷區(qū)的個(gè)數(shù);M為堆棧生長(zhǎng)經(jīng)過(guò)雷區(qū)的個(gè)數(shù)。圖5 一個(gè)堆棧生長(zhǎng)實(shí)例Fig.5 An instance for growth of stack
在線(xiàn)程創(chuàng)建時(shí),一般需要指定其分配堆??臻g的大小,系統(tǒng)為線(xiàn)程分配堆棧后,軟件記錄堆棧的起始地址和分配深度。本文方法中,對(duì)于線(xiàn)程分配的堆棧要求其大小為1 Kbyte的整數(shù)倍,便于后續(xù)劃分和處理,也可以按照其他單位長(zhǎng)度進(jìn)行分配,本文以1 Kbyte為示例進(jìn)行說(shuō)明。
將分配的堆棧空間進(jìn)行劃分,具體分為以下3個(gè)部分,見(jiàn)圖5左半部分。
(1)使用區(qū):初始大小為2 Kbyte,起始地址。該區(qū)域?yàn)檐浖_\(yùn)行時(shí)允許使用的堆棧區(qū)域。使用區(qū)域的頂部為當(dāng)前棧頂,軟件需要單獨(dú)記錄堆棧當(dāng)前棧頂,由于使用區(qū)初始設(shè)置為2 Kbyte,因此初始記錄的當(dāng)前棧頂為起始地址減0x800;
(2)隔離區(qū):位于堆棧分配空間的最后1 Kbyte,隔離區(qū)的起始地址等于堆棧區(qū)的起始地址減分配深度,再加0x400;
(3)雷區(qū):位于使用區(qū)和隔離區(qū)之間,每個(gè)雷區(qū)占用1 Kbyte的空間。
正常情況下,軟件在對(duì)堆??臻g進(jìn)行初始化時(shí),將其全部初始化為0。本文方法中,同樣將雷區(qū)和隔離區(qū)中的數(shù)據(jù)均初始化為0,32 bit數(shù)據(jù)0對(duì)應(yīng)的7 bit EDAC校驗(yàn)為0xC,利用處理器的造錯(cuò)機(jī)制,將實(shí)際寫(xiě)入存儲(chǔ)器的校驗(yàn)修改為0x0,使其存在雙比特錯(cuò)誤。軟件通過(guò)操作BM3803的存儲(chǔ)器容錯(cuò)配置寄存器1,2,3,可以對(duì)指定地址32 bit數(shù)據(jù)位和7 bit校驗(yàn)位進(jìn)行造錯(cuò)。通過(guò)軟件設(shè)置存儲(chǔ)器容錯(cuò)配置寄存器1的EWB位使能寫(xiě)旁路,并設(shè)置存儲(chǔ)器容錯(cuò)配置寄存器2為0x0(即數(shù)據(jù)位不翻轉(zhuǎn))、存儲(chǔ)器容錯(cuò)配置寄存器3為0xC(即校驗(yàn)的第2位和第3位進(jìn)行翻轉(zhuǎn)),然后對(duì)雷區(qū)和隔離區(qū)進(jìn)行清0操作,實(shí)際寫(xiě)入的32 bit數(shù)據(jù)為0x0,實(shí)際寫(xiě)入的校驗(yàn)為0x0,即7 bit校驗(yàn)按照設(shè)置,其中第2位和第3位發(fā)生了翻轉(zhuǎn)。
軟件對(duì)堆棧進(jìn)行初始化時(shí),將堆棧中使用區(qū)初始空間進(jìn)行清0。使用上述方法,將雷區(qū)和隔離區(qū)的空間全部清0,使雷區(qū)和隔離區(qū)中的所有32 bit數(shù)據(jù)0存在雙比特錯(cuò)誤。
在初始化堆棧完成后,線(xiàn)程進(jìn)入正常運(yùn)行狀態(tài),隨著軟件調(diào)用函數(shù)等操作,堆棧逐漸生長(zhǎng),其使用范圍向低地址擴(kuò)展,當(dāng)使用范圍超過(guò)使用區(qū)(初始2 Kbyte)后,進(jìn)入雷區(qū)。由于雷區(qū)內(nèi)雙比特錯(cuò)誤的存在,程序運(yùn)行對(duì)堆棧使用時(shí)的操作類(lèi)型受到了極大的限制,不可隨意進(jìn)行讀寫(xiě)操作,如果程序?qū)讌^(qū)任意地址進(jìn)行字節(jié)或半字寫(xiě)操作,該操作會(huì)立即引發(fā)處理器異常,產(chǎn)生0x2B陷阱。
軟件進(jìn)入陷阱處理程序后,通過(guò)讀取失效地址寄存器獲取發(fā)生陷阱的錯(cuò)誤地址。通過(guò)地址范圍比對(duì)確認(rèn)錯(cuò)誤地址所在的范圍。①將該地址和各個(gè)線(xiàn)程的堆棧地址范圍進(jìn)行比較,確定錯(cuò)誤地址是否在線(xiàn)程堆棧區(qū)域中。②如果錯(cuò)誤地址屬于某線(xiàn)程堆棧范圍,則將錯(cuò)誤地址與軟件記錄的當(dāng)前棧頂進(jìn)行比較。若錯(cuò)誤地址小于當(dāng)前棧頂,且大于隔離區(qū)首地址,則確定錯(cuò)誤發(fā)生在雷區(qū);錯(cuò)誤地址小于隔離區(qū)首地址,則確定錯(cuò)誤發(fā)生在隔離區(qū)。否則,錯(cuò)誤發(fā)生在堆棧正常工作區(qū)域,屬于其他問(wèn)題引起的錯(cuò)誤,不在本文的討論范圍內(nèi)。
對(duì)于發(fā)生在雷區(qū)的錯(cuò)誤進(jìn)行以下操作:①關(guān)閉EDAC校驗(yàn)功能;②將當(dāng)前棧頂減0x400,即釋放一個(gè)雷區(qū),使當(dāng)前棧頂向上生長(zhǎng);③按照4 byte地址對(duì)齊,依次讀取該雷區(qū)中的所有數(shù)據(jù),每讀取一個(gè)32 bit數(shù)據(jù),將其寫(xiě)回原地址(由于回寫(xiě)過(guò)程沒(méi)有啟用造錯(cuò)機(jī)制,因此實(shí)際寫(xiě)入的32 bit數(shù)據(jù)為存儲(chǔ)器中原來(lái)實(shí)際存儲(chǔ)的數(shù)據(jù),實(shí)際寫(xiě)入的7 bit校驗(yàn)為32 bit數(shù)據(jù)對(duì)應(yīng)的校驗(yàn),即雷區(qū)內(nèi)的雙比特錯(cuò)誤全部被清除)。④若當(dāng)前棧頂小于錯(cuò)誤地址,則表示當(dāng)前堆棧使用區(qū)已經(jīng)覆蓋程序被訪(fǎng)問(wèn)空間,則使能EDAC校驗(yàn)功能,退出陷阱處理。否則,跳轉(zhuǎn)至②繼續(xù)執(zhí)行。
對(duì)于發(fā)生在隔離區(qū)的錯(cuò)誤,軟件可以根據(jù)需要自行制定故障恢復(fù)策略,如線(xiàn)程復(fù)位、等待狗咬復(fù)位或切換至備份設(shè)備等。
對(duì)于堆棧的當(dāng)前使用深度,在軟件實(shí)時(shí)運(yùn)行過(guò)程中,可以通過(guò)訪(fǎng)問(wèn)各個(gè)線(xiàn)程的當(dāng)前棧頂,計(jì)算得出各個(gè)線(xiàn)程堆棧的使用深度(使用區(qū)的起始地址減去使用區(qū)的當(dāng)前棧頂,精確到1 Kbyte),堆棧占用率為使用深度除以分配深度,再乘以100%。
本文方法經(jīng)軟件代碼實(shí)現(xiàn)后,對(duì)其運(yùn)行性能和精度在目標(biāo)硬件平臺(tái)上進(jìn)行測(cè)試驗(yàn)證。目標(biāo)系統(tǒng)處理器采用BM3803平臺(tái),主頻設(shè)置為30 MHz,分別對(duì)初始化模塊和陷阱處理模塊進(jìn)行性能測(cè)試,測(cè)試結(jié)果如表1所示。從性能測(cè)試結(jié)果可以看出,與堆棧清0操作的初始化模塊相比,本文方法中的初始化模塊時(shí)延幾乎沒(méi)有增加。另外,由于軟件啟動(dòng)后所有堆棧僅初始化1次,因此該延時(shí)幾乎可以忽略不計(jì)。多數(shù)情況下,陷阱處理模塊每次都只需要清掃1個(gè)雷區(qū)便可以退出,每個(gè)雷區(qū)至多進(jìn)行一次清掃,因此558 μs的處理時(shí)間對(duì)系統(tǒng)性能基本沒(méi)有影響。
表1 模塊性能測(cè)試結(jié)果
以某綜合電子系統(tǒng)星載軟件為被測(cè)對(duì)象,利用本文方法對(duì)其創(chuàng)建的8個(gè)線(xiàn)程的堆棧使用情況進(jìn)行測(cè)試,并與采用動(dòng)態(tài)測(cè)試工具的測(cè)試結(jié)果進(jìn)行比對(duì),測(cè)試結(jié)果如表2所示。
表2 堆棧使用深度測(cè)試結(jié)果
本文選取上述測(cè)試用例中堆棧使用深度最大的線(xiàn)程進(jìn)行堆棧溢出測(cè)試,測(cè)試前將該線(xiàn)程堆棧分配從32 Kbyte縮小為9 Kbyte。測(cè)試程序陷阱處理對(duì)堆棧溢出的線(xiàn)程進(jìn)行掛起操作,并通過(guò)串口打印產(chǎn)生陷阱的地址、程序指針和線(xiàn)程ID。運(yùn)行測(cè)試程序后,打印串口輸出如圖6所示。查看程序指針(操作雷區(qū))地址0x4001BD54對(duì)應(yīng)的函數(shù)為Insert_Pk_To_Vchannel,操作雷區(qū)的匯編指令為對(duì)一個(gè)字節(jié)地址清0。觸發(fā)陷阱的雷區(qū)地址為0x403E9422,位于編號(hào)為1的線(xiàn)程分配的堆棧區(qū)最后1 Kbyte內(nèi),即該地址處于隔離區(qū),因此觸發(fā)陷阱,線(xiàn)程被掛起。
圖6 測(cè)試結(jié)果輸出Fig.6 Output of test result
上述測(cè)試結(jié)果表明:采用本文方法可以獲得堆棧的使用深度,其精確度為1 Kbyte,比動(dòng)態(tài)測(cè)試工具測(cè)量數(shù)據(jù)的精確度差,但就堆棧余量觀察而言,該精度已經(jīng)可以滿(mǎn)足使用要求。另外,本文方法和大多數(shù)動(dòng)態(tài)測(cè)試方法相同,如果無(wú)法提供使軟件達(dá)到最大堆棧使用深度的測(cè)試用例,便無(wú)法測(cè)量堆棧最大使用深度。不同的是,本文方法可以在線(xiàn)實(shí)時(shí)獲取堆棧使用深度,并且溢出前可以提供溢出故障現(xiàn)場(chǎng)的關(guān)鍵信息(包括溢出地址、線(xiàn)程ID和溢出時(shí)的程序指針等),這些故障現(xiàn)場(chǎng)信息對(duì)后續(xù)的故障排查工作至關(guān)重要。
星載軟件在其運(yùn)行過(guò)程中難以感知堆棧的生長(zhǎng)過(guò)程,不能確定其使用余量是否充足,也難以對(duì)堆棧溢出故障進(jìn)行定位。本文提出了一種應(yīng)用EDAC容錯(cuò)技術(shù)的堆棧溢出實(shí)時(shí)檢測(cè)方法,并以采用BM3803處理器的星載軟件為例,通過(guò)在堆棧中設(shè)置多個(gè)雷區(qū),使軟件具備感知堆棧動(dòng)態(tài)生長(zhǎng)并實(shí)時(shí)計(jì)算堆棧占用率的能力,在堆棧棧頂設(shè)置隔離區(qū),使軟件具備提前獲知堆棧溢出的能力。本文方法的優(yōu)點(diǎn)在于:軟件可以實(shí)時(shí)獲取當(dāng)前堆棧使用深度(精度精確到1 Kbyte);原理簡(jiǎn)單,容易實(shí)現(xiàn),實(shí)現(xiàn)后軟件可以在線(xiàn)、實(shí)時(shí)進(jìn)行檢測(cè)。與文獻(xiàn)[3]中的方法相比,本文方法無(wú)需通過(guò)在軌上注測(cè)試程序,無(wú)需長(zhǎng)期在軌周期性運(yùn)行,因此對(duì)星上軟件運(yùn)行與在軌維護(hù)影響更少。而且,采用本文方法時(shí),軟件在堆棧溢出時(shí)會(huì)立即由陷阱處理程序接管,從而消除堆棧崩潰后軟件行為的不確定性,極大簡(jiǎn)化系統(tǒng)故障處理應(yīng)對(duì)措施的設(shè)計(jì),提升軟件的可靠性。