高艷鹍,劉 華,劉朝暉
(1.北京計(jì)算機(jī)技術(shù)及應(yīng)用研究所,北京 100039;2.南華大學(xué),湖南 衡陽 421001)
傳統(tǒng)的微處理器不適用于進(jìn)行數(shù)字信號處理所需要的高等數(shù)學(xué)運(yùn)算,其運(yùn)算需要較長的計(jì)算時(shí)間,不能滿足現(xiàn)代信號實(shí)時(shí)處理的要求。而DSP所具有的系統(tǒng)構(gòu)成靈活、可編程、使用面廣的特點(diǎn),使其在通信、航空航天、醫(yī)療儀器、工業(yè)控制及信息家電中成為不可或缺的數(shù)字信息處理的計(jì)算引擎。TMS320C6678(簡稱C6678)是美國德州儀器(Texas Instrument)于2011年10月推出的一款高性能浮點(diǎn)嵌入式數(shù)字信號處理器(DSP)[1]。作為一款8核的DSP,C6678可以滿足在軍事、工業(yè)等領(lǐng)域的實(shí)時(shí)數(shù)據(jù)處理的性能要求。C6678提供了多種芯片外設(shè)接口,支持多樣化總線協(xié)議,包括RapidIO、PCIe、I2C、EMIF、UART、SPI總線及千兆網(wǎng),GPIO、TSIP等[2]。
天熠嵌入式操作系統(tǒng)彈載版是航天二院706所針對彈上計(jì)算機(jī)開發(fā)的嵌入式操作系統(tǒng)產(chǎn)品,目前針對TI的C6000系列DSP能夠支持C6713、C6672、C6678,提供多種基礎(chǔ)的內(nèi)核服務(wù)。目前,使用TI公司的TMS系列通用DSP處理器做系統(tǒng)開發(fā)需要使用專門的開發(fā)工具CCS,通過JTAG接口的仿真器將程序下載到目標(biāo)機(jī)調(diào)試運(yùn)行。在某些比較復(fù)雜的應(yīng)用背景下,需要將用戶應(yīng)用程序通過靜態(tài)鏈接生成一個(gè)獨(dú)立的可執(zhí)行鏡像下載執(zhí)行,用戶每次修改一行程序都需要將整個(gè)程序重新編譯下載。當(dāng)可執(zhí)行程序較大時(shí),下載過程就會消耗用戶較長的時(shí)間,效率不高。
在商業(yè)的VxWorks操作系統(tǒng)上已經(jīng)針對X86、PowerPC等處理器實(shí)現(xiàn)了應(yīng)用程序軟件模塊的動態(tài)加載機(jī)制,先將內(nèi)核運(yùn)行起來,然后通過內(nèi)核加載應(yīng)用程序。當(dāng)應(yīng)用程序修改后,也只需要先卸載已經(jīng)在系統(tǒng)中的該軟件模塊,再重新加載模塊,大大提高了開發(fā)的效率。動態(tài)加載機(jī)制的核心技術(shù)是延遲鏈接,涉及到處理器的體系結(jié)構(gòu)、編譯器技術(shù)、可執(zhí)行程序的ABI(Application Binary Interface)三方面內(nèi)容,目前業(yè)界尚未有商用的操作系統(tǒng)能夠針對TI的DSP處理器平臺提供動態(tài)加載機(jī)制。為在彈載領(lǐng)域滿足用戶的使用,在基于TI C6678 DSP處理器的天熠嵌入式操作系統(tǒng)中增加動態(tài)加載機(jī)制做了技術(shù)探索,并研發(fā)了動態(tài)加載器作為天熠嵌入式操作系統(tǒng)產(chǎn)品組件。
隨著設(shè)備集成度越來越高,功能越來越復(fù)雜,在一個(gè)設(shè)備上運(yùn)行的軟件往往需要不同的廠商提供軟件模塊一起配合運(yùn)行,各模塊提供的服務(wù)往往又需要被其他模塊調(diào)用,例如模塊A作為主體應(yīng)用模塊需要調(diào)用模塊B提供的濾波計(jì)算服務(wù),調(diào)用模塊C提供的設(shè)備服務(wù),模塊B在執(zhí)行濾波計(jì)算時(shí)又需要操作系統(tǒng)提供的內(nèi)存服務(wù)接口的存儲器分配功能。
傳統(tǒng)的開發(fā)方式需要在軟件開發(fā)時(shí)將不同的軟件模塊靜態(tài)鏈接成一個(gè)單獨(dú)的可執(zhí)行程序并下載到目標(biāo)機(jī)上[3]。該方式的一個(gè)缺陷是需要不同的廠商提供對應(yīng)模塊的源碼,基于知識產(chǎn)權(quán)的保護(hù),廠商一般會將源碼封裝為靜態(tài)庫的形式提供給主程序的研制方,但需要幾方廠商必須同時(shí)開發(fā)完成并提交成果,否則將無法編譯鏈接[4]。在開發(fā)時(shí)不同廠商軟件產(chǎn)品的難度,需要的資源往往無法協(xié)調(diào)一致,進(jìn)度無法保證,作為需要使用其他模塊的主應(yīng)用則無法構(gòu)建出一個(gè)完整的可執(zhí)行應(yīng)用,及早地開展自己這部分的測試工作。軟件模塊獨(dú)立運(yùn)行視圖和傳統(tǒng)靜態(tài)鏈接的程序運(yùn)行視圖如圖1。
圖1 兩種運(yùn)行方式視圖對比Fig.1 Comparison of two operation modes
通常在調(diào)試階段,程序開發(fā)人員對DSP進(jìn)行編程。首先,是在PC機(jī)上使用DSP廠商提供的調(diào)試開發(fā)軟件平臺編譯程序,而后通過DSP板的JTAG調(diào)試接口將程序下載到DSP中運(yùn)行。在實(shí)際應(yīng)用中,通常需要將DSP程序固化在DSP板上FLASH或者EEPROM中,系統(tǒng)上電后,程序自動從FLASH中加載至DSP內(nèi)部存儲區(qū)并且執(zhí)行。但上述這兩種方法都需要使用額外的JTAG線來連接主機(jī)和DSP板,對于已經(jīng)裝配完畢的密封設(shè)備,如果需要更改程序,必須將設(shè)備進(jìn)行拆裝,重新安裝JTAG線進(jìn)行調(diào)試[5]。從工程應(yīng)用的角度來看,頻繁地對已經(jīng)裝配完畢的設(shè)備進(jìn)行拆裝,會嚴(yán)重影響整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。為了避免這種情況的發(fā)生,應(yīng)選擇在不拆裝設(shè)備的同時(shí)遠(yuǎn)程對DSP板上程序進(jìn)行動態(tài)加載[6]。
現(xiàn)階段,基于現(xiàn)有的同外部設(shè)備連接的總線接口進(jìn)行程序下載的主要技術(shù)實(shí)現(xiàn)通過在目標(biāo)機(jī)上固化一小段引導(dǎo)程序bootloader,啟動后引導(dǎo)程序負(fù)責(zé)目標(biāo)機(jī)的運(yùn)行環(huán)境的初始化,并通過外部設(shè)備接口(一般為串口)同宿主機(jī)進(jìn)行交互,下載應(yīng)用程序并固化到Flash上,并通過跳轉(zhuǎn)語句將處理器執(zhí)行的控制權(quán)交給應(yīng)用程序[7]。
該種方式確實(shí)可以滿足應(yīng)用程序升級的需求,但有兩點(diǎn)不足:第一點(diǎn)是引導(dǎo)程序和應(yīng)用程序?qū)?nèi)存的使用布局需要在開發(fā)時(shí)通過鏈接腳本協(xié)同規(guī)劃,從而兩個(gè)程序相互依賴;第二點(diǎn)是被加載的對象只能是獨(dú)立的可執(zhí)行應(yīng)用程序,bootloader無法加載多個(gè)應(yīng)用[8]。
在一些高可靠的控制計(jì)算機(jī)系統(tǒng)設(shè)計(jì)階段會提出可重構(gòu)設(shè)計(jì)需求,完成功能定制、動態(tài)升級、故障恢復(fù)。目前,筆者接觸到的對于長期在軌運(yùn)行的星載計(jì)算機(jī)領(lǐng)域?qū)υ撔枨笥葹槠惹?。例如,某星載設(shè)備的相機(jī)控制系統(tǒng)是針對地面物體進(jìn)行拍攝的高端相機(jī)的核心控制部件,相機(jī)對調(diào)焦的控制算法需要根據(jù)地物高度、衛(wèi)星在軌位置、速度、姿態(tài)等相關(guān)因素計(jì)算調(diào)焦距離[9]。由于是首次研制,缺少歷史的相關(guān)試驗(yàn)數(shù)據(jù),對最終的相機(jī)成像效果通過地面試驗(yàn)及仿真不能充分有效驗(yàn)證,無法確定在衛(wèi)星發(fā)射后載荷相機(jī)對地面物體的成像能否達(dá)到預(yù)定的目標(biāo)?;诖?,研制方設(shè)計(jì)了相機(jī)控制系統(tǒng)的在軌升級機(jī)制,相機(jī)的控制系統(tǒng)使用的C5000的DSP處理器,上電啟動后先運(yùn)行一段引導(dǎo)程序,由引導(dǎo)程序判斷是否從遙測控制接收到對控制程序的在軌更新命令。當(dāng)接收到命令時(shí),更新核心的控制程序[10]。但設(shè)計(jì)中存在一個(gè)不足:需要控制計(jì)算機(jī)的重新上電,運(yùn)行引導(dǎo)程序。
通過以上幾點(diǎn)分析,在由TI C6000 DSP構(gòu)成的復(fù)雜、高可靠嵌入式系統(tǒng)中引入基于動態(tài)鏈接機(jī)制的動態(tài)加載解決方案有多方面的工程需求。在以天熠嵌入式操作系統(tǒng)彈載版的基礎(chǔ)上,開展了研究和設(shè)計(jì)。
完成動態(tài)加載及鏈接牽涉不同方面的計(jì)算機(jī)技術(shù),其中一個(gè)必要條件是編譯器必須能夠支持生成延遲重定位的目標(biāo)程序。這需要編譯器在編譯時(shí)能夠暫時(shí)屏蔽掉未定址的符號而不會報(bào)編譯錯(cuò)誤,同時(shí)為生成地址無關(guān)的代碼要插入特定的樁代碼形式或過程鏈接表。該目標(biāo)程序同可執(zhí)行程序的區(qū)別是可執(zhí)行程序是完全具備執(zhí)行能力的程序,不需要任何其他模塊輔助支持。而動態(tài)庫程序本身不具備執(zhí)行能力,其中存在引用的符號處于未定址的狀態(tài),如果強(qiáng)制執(zhí)行,則處理器會進(jìn)入到異常狀態(tài)。
根據(jù)TI的編譯器手冊說明能夠生成支持動態(tài)鏈接庫的ELF文件格式的編譯器版本應(yīng)至少為7.2以上。目前國內(nèi)針對C6000處理器使用的開發(fā)環(huán)境主要為CCS3.3和CCS5.5。其中,CCS3.3中使用的Cl6x編譯器版本為6.08,CCS5.5的Cl6x編譯器版本為7.4,基于CCS3.3開發(fā)環(huán)境無法實(shí)現(xiàn)動態(tài)鏈接庫生成。
動態(tài)鏈接庫的加載過程緊密依賴于TI定義的EABI(ELF Application Binary Interface)文件格式。該格式是TI公司根據(jù)標(biāo)準(zhǔn)ELF文件格式自定義的一種文件格式,依據(jù)其標(biāo)準(zhǔn)定義主要分為3類,具體見表1。
表1 ELF文件的主要類型Table 1 Main types of ELF files
目前,可使用的文件格式為共享目標(biāo)文件。從連接和運(yùn)行的角度,可以分別把目標(biāo)文件的組成部分劃分為以下兩種視圖,具體如圖2。
圖2 連接視圖和運(yùn)行視圖對比Fig.2 Comparison of connection view and operation view
表2為ELF文件格式中各段的作用,在實(shí)際加載過程都是以“段”作為處理對象。
表2 ELF文件格式的各段作用和說明Table 2 Functions and descriptions of each paragraph of ELF file format
目標(biāo)模塊被加載到內(nèi)存空間后,不能立即運(yùn)行。動態(tài)加載機(jī)制還須對目標(biāo)模塊進(jìn)行處理,解決模塊的外部引用(符號解析)和重定位,這一步是動態(tài)加載過程中的最關(guān)鍵環(huán)節(jié)。由于模塊是被單獨(dú)編譯成共享目標(biāo)文件,因而在生成目標(biāo)文件時(shí),調(diào)用的其他模塊或函數(shù)庫中的函數(shù)和全局變量(統(tǒng)稱為符號)的地址仍處于不確定狀態(tài)。以對全局函數(shù)的引用舉例說明編譯器插入的stub形式及重定址過程。源碼如下,import關(guān)鍵字標(biāo)識其為導(dǎo)入的符號,其作用是指示編譯器該符號需要加載時(shí)綁定地址。
該源碼編譯成add.dll后查看其反匯編的代碼:
在目標(biāo)模塊中,對.plt段中sym符號的地址以0x0000的形式寫入到MVK指令的地址碼,并通過B10寄存器直接尋址跳轉(zhuǎn)到sym函數(shù),而.text段中add函數(shù)對sym符號的引用通過對.plt段的相對尋址訪問(兩個(gè)端加載后的相對偏移量保持不變)。在重定址時(shí)只需根據(jù)sym符號的實(shí)際地址和記錄的引用位置修改MVK指令的地址碼。
3.1.1 動態(tài)加載器的分層設(shè)計(jì)
動態(tài)加載管理采用分層式設(shè)計(jì),由用戶接口層、管理層、執(zhí)行層3個(gè)層次組成,如圖3。
圖3 動態(tài)加載器的分層結(jié)構(gòu)Fig.3 Hierarchical structure of dynamic loader
1)用戶接口層:負(fù)責(zé)同用戶進(jìn)行交互的界面,提供用戶關(guān)于動態(tài)加載相關(guān)命令的輸入、解析、執(zhí)行,具體見表3。
表3 用戶接口的主要命令集合Table 3 Main command sets of user interface
2)管理層:管理層主要利用內(nèi)核提供的多種服務(wù)加載模塊,對加載模塊的ELF文件格式有效性進(jìn)行判斷,保存和維護(hù)模塊的相關(guān)信息,并形成加載模塊的關(guān)系鏈表。
3)執(zhí)行層:主要負(fù)責(zé)對加載模塊外部引用的全局符號進(jìn)行重定位,完成動態(tài)鏈接過程。
3.1.2 與天熠操作系統(tǒng)內(nèi)核其他服務(wù)的關(guān)系
考慮到天熠嵌入式操作系統(tǒng)自身的微內(nèi)核架構(gòu),動態(tài)加載器在設(shè)計(jì)上規(guī)劃為一個(gè)獨(dú)立的操作系統(tǒng)組件,可以跟隨系統(tǒng)進(jìn)行功能裁剪,并充分利用天熠操作系統(tǒng)已有的系統(tǒng)服務(wù)完成動態(tài)加載器的設(shè)計(jì),如圖4。
圖4 內(nèi)核組件服務(wù)的調(diào)用過程Fig.4 Calling process of kernel component service
Shell組件先使用網(wǎng)絡(luò)組件提供的ftp服務(wù)、內(nèi)存管理組件、文件系統(tǒng)組件將軟件模塊下載到文件系統(tǒng),然后再通過動態(tài)加載組件完成模塊的動態(tài)加載及動態(tài)鏈接。
3.2.1 內(nèi)存布局規(guī)劃
嵌入式系統(tǒng)資源使用一般規(guī)劃的比較嚴(yán)格,尤其在內(nèi)存使用方面。DSP程序的開發(fā)需要通過cmd文件的鏈接腳本預(yù)先規(guī)劃好內(nèi)存的整體布局。主機(jī)系統(tǒng)對動態(tài)庫的加載主要是由進(jìn)程控制塊來維護(hù)整個(gè)32位虛擬地址空間的內(nèi)存使用,通過查詢空閑空間找到未使用區(qū)域后,再加載動態(tài)庫。因?yàn)閯討B(tài)庫加載后隨著應(yīng)用生命周期一直運(yùn)行,不會像堆數(shù)據(jù)空間的內(nèi)容、大小經(jīng)常性變化?;谝陨咸攸c(diǎn),采取在鏈接腳本中規(guī)劃出固定大小的內(nèi)存空間BLOB區(qū)域?qū)S糜趧討B(tài)庫加載。加載器會計(jì)算加載的模塊總共占用內(nèi)存空間的大小,當(dāng)預(yù)先規(guī)劃的加載空間不足時(shí)會通過Shell向用戶提示,需要用戶重新規(guī)劃內(nèi)存空間布局。
鏈接腳本如下:
3.2.2 加載的模塊管理
加載器的功能主要是完成動態(tài)庫文件的加載,包括ELF文件頭解析,文件格式有效性判斷。對實(shí)際加載的模塊信息進(jìn)行維護(hù),包括文件頭、程序頭、加載段等信息,為后續(xù)的模塊卸載、依賴性分析提供支撐。描述模塊描述信息的結(jié)構(gòu)體定義如圖5,并通過DLIMP_Loaded_Module*loaded_module指針建立已加載模塊的維護(hù)鏈表快速遍歷模塊的相關(guān)信息。
圖5 加載模塊的管理Fig.5 Load module management
3.2.3 加載的段管理
依據(jù)ELF文件格式定義,其包含了諸多段,但并不是所有段對模塊的實(shí)際運(yùn)行有作用。根據(jù)內(nèi)存訪問模型和編譯器選項(xiàng),在動態(tài)加載過程中需要加載同實(shí)際運(yùn)行相關(guān).plt、.got、.data、.text段,詳見表4。
表4 加載段的作用Table 4 Functions of loading section
最終加載到實(shí)際運(yùn)行內(nèi)存空間的段視圖,如圖6。
圖6 模塊加載后的.BLOB塊內(nèi)存視圖Fig.6 BLOB Block memory view after module loading
動態(tài)鏈接器將dll模塊加載完成后,根據(jù)導(dǎo)入符號的名稱遍歷查找已加載的Base映像的導(dǎo)出符號,將符號地址填入到.plt、.got段中,使已加載的模塊具備執(zhí)行能力,如圖7。
圖7 重定址過程Fig.7 Re addressing process
本文通過分析在嵌入式系統(tǒng)中軟件模塊動態(tài)加載的使用需求和動態(tài)鏈接原理,并在天熠嵌入式操作系統(tǒng)的產(chǎn)品中實(shí)現(xiàn)了基于C6678處理器的軟件模塊動態(tài)加載組件。該組件可以完成多個(gè)用戶定義模塊的自動(文件系統(tǒng))、手動(網(wǎng)絡(luò)或串口終端)加載及卸載,可用于支持嵌入式系統(tǒng)的功能重構(gòu)、產(chǎn)品的動態(tài)升級、故障恢復(fù),最終使整個(gè)系統(tǒng)擴(kuò)展性和靈活性大大提高,較好地滿足了用戶的實(shí)際需要。