任艷艷, 翟高壽, 張俊紅
1(北京交通大學(xué) 計算機與信息技術(shù)學(xué)院, 北京 100044)2(北京建筑大學(xué) 電氣與信息工程學(xué)院, 北京 100044)
設(shè)備驅(qū)動程序通常會占到70%以上份額的操作系統(tǒng)內(nèi)核源碼, 且設(shè)備驅(qū)動程序的更新維護往往會牽涉到超過35%的源碼修改, 故而保持設(shè)備驅(qū)動程序與操作系統(tǒng)內(nèi)核不斷變化的其余部分的一致性是目前操作系統(tǒng)內(nèi)核開發(fā)的一項難題[1]. 伴隨Linux內(nèi)核版本更新升級速度的提升, 其內(nèi)核穩(wěn)定版本的發(fā)布周期已縮短至不到30天. 對于比較依賴內(nèi)核接口的驅(qū)動程序來說, 由內(nèi)核更新變化而引發(fā)的設(shè)備驅(qū)動的更新操作亦即設(shè)備驅(qū)動的協(xié)同演化將會更加頻繁, 所以實現(xiàn)設(shè)備驅(qū)動程序的自動更新就顯得尤為迫切. 換句話說, 若是設(shè)備驅(qū)動程序能夠最大化地實現(xiàn)自動更新, 就可在更大程度上減少協(xié)同演化所耗費的時間, 進而提高設(shè)備驅(qū)動程序更新效率. 作為占據(jù)內(nèi)核源碼較大比重且是操作系統(tǒng)內(nèi)核漏洞主要來源的驅(qū)動代碼, 設(shè)備驅(qū)動程序自動更新還可以提高驅(qū)動代碼的正確性, 從而進一步提高操作系統(tǒng)的安全性和健壯性.
現(xiàn)在尚未有工具可以完整實現(xiàn)設(shè)備驅(qū)動程序的自動更新, 不過已有相關(guān)的差異分析工具和差異應(yīng)用工具. 常見的差異分析工具有 Unix diff, 還有 Python dfflib等, 都是基于文本的差異比較工具, 也有以函數(shù)為粒度的內(nèi)核差異比較工具DIFFE[2], 還有基于語法樹的內(nèi)核差異分析工具[3,4], 雖然都可以進行驅(qū)動代碼差異比較, 但也僅僅是差異比較, 而且是基于已知兩版驅(qū)動代碼的比較. 差異應(yīng)用工具如 Coccinelle[5–9], 此項目最初被設(shè)計于解決Linux 內(nèi)核與驅(qū)動協(xié)同演化問題,設(shè)計理念是內(nèi)核開發(fā)人員編寫區(qū)別于傳統(tǒng)補丁的語義補丁, 說明兩個版本的變動信息, 然后由驅(qū)動開發(fā)人員使用Coccinelle應(yīng)用此補丁, 完成內(nèi)核驅(qū)動移植工作,但這沒有被Linux內(nèi)核接受, 如今Coccinelle轉(zhuǎn)而專注于在內(nèi)核中發(fā)現(xiàn)并修復(fù)漏洞.
為了改變設(shè)備驅(qū)動程序更新操作現(xiàn)狀, 本文研究了Linux設(shè)備驅(qū)動程序自動更新方法, 設(shè)計并實現(xiàn)了一組相應(yīng)的自動更新支撐工具, 包括設(shè)備驅(qū)動程序?qū)?nèi)核依賴接口分析工具、依賴接口差異性分析工具以及設(shè)備驅(qū)動更新輔助信息生成工具, 這組工具統(tǒng)稱為設(shè)備驅(qū)動自動更新輔助工具(Device Driver Assistant Updater, DDAU), 以便輔助用戶解決隨著內(nèi)核更新帶來的驅(qū)動更新問題, 源代碼可在GitHub上獲取.DDAU實現(xiàn)設(shè)備驅(qū)動的更新過程, 是通過A、B兩版Linux 內(nèi)核源碼, A 版更新后是 B 版. 其中, A 版是完整版內(nèi)核源碼, B版在全文中沒有特別說明的情況下默認(rèn)都不含有設(shè)備驅(qū)動代碼. 設(shè)備驅(qū)動更新就是, 根據(jù)A版內(nèi)核設(shè)備驅(qū)動接口依賴分析和設(shè)備驅(qū)動依賴接口在A、B兩版本之間的差異分析, 得到A版內(nèi)核設(shè)備驅(qū)動依賴接口差異信息, 然后根據(jù)此差異信息就可以得到設(shè)備驅(qū)動程序必須要做出哪些改動才能和內(nèi)核變化保持一致, 這些必須要改動的信息或者可能需要改動的信息就是DDAU生成的設(shè)備驅(qū)動更新輔助信息.設(shè)備驅(qū)動依賴接口分析就是分析設(shè)備驅(qū)動程序?qū)?nèi)核接口的依賴關(guān)系, 提取設(shè)備驅(qū)動依賴的內(nèi)核接口, 包括內(nèi)核數(shù)據(jù)、內(nèi)核函數(shù)、宏以及驅(qū)動對它們的調(diào)用關(guān)系.Linux設(shè)備驅(qū)動更新問題模型如圖1所示.
圖1 Linux 設(shè)備驅(qū)動更新問題模型
Linux設(shè)備驅(qū)動程序在操作系統(tǒng)中扮演著特殊的角色, 其完全隱藏了設(shè)備工作的細(xì)節(jié), 只為操作系統(tǒng)提供定義好的內(nèi)部編程接口, 操作系統(tǒng)通過這些接口調(diào)用驅(qū)動以達(dá)到操作實際硬件的目的. 因此設(shè)備驅(qū)動程序是操作系統(tǒng)和硬件的溝通橋梁, 它位于操作系統(tǒng)和硬件之間, 是直接和硬件打交道的軟件程序, 設(shè)備驅(qū)動與操作系統(tǒng)內(nèi)核關(guān)系如圖2所示. 設(shè)備驅(qū)動依賴于操作系統(tǒng)內(nèi)核接口, 因而隨著Linux內(nèi)核升級帶來的內(nèi)核接口變化, 設(shè)備驅(qū)動也必須協(xié)同更新, 以便設(shè)備驅(qū)動能和操作系統(tǒng)內(nèi)核保持一致性, 才能和內(nèi)核進行正確的信息交互.
圖2 設(shè)備驅(qū)動與操作系統(tǒng)內(nèi)核關(guān)系示意圖
在Linux內(nèi)核中, 設(shè)備驅(qū)動程序一般以內(nèi)核可加載模塊(LKM)的方式存在, 可以在內(nèi)核編譯的時候靜態(tài)加載, 因此單個設(shè)備驅(qū)動的更新只需要更改相對應(yīng)的驅(qū)動目錄里的相關(guān)文件. 內(nèi)核和設(shè)備驅(qū)動的信息交互通過宏和函數(shù), 數(shù)據(jù)的傳遞一般是通過結(jié)構(gòu)體等數(shù)據(jù)類型, 因此在分析接口依賴關(guān)系的時候, 著重在函數(shù)、宏, 以及數(shù)據(jù)類型如結(jié)構(gòu)體等接口分析上, 設(shè)備驅(qū)動接口分析是圖2中操作系統(tǒng)內(nèi)核部分的設(shè)備驅(qū)動內(nèi)核依賴接口部分的分析.
對于應(yīng)用程序來說, 它因?qū)崿F(xiàn)某個功能需要而調(diào)用系統(tǒng)提供的函數(shù)時, 它并不關(guān)心這個函數(shù)內(nèi)部的實現(xiàn)細(xì)節(jié), 只需要知道要調(diào)用的函數(shù)名字, 要傳入的參數(shù)以及返回值類型. 同樣地, 對于驅(qū)動來說, 驅(qū)動更新需要了解內(nèi)核接口的名字、參數(shù)以及返回值的類型發(fā)生了哪些改變. 接口改變主要包括函數(shù)、宏, 以及數(shù)據(jù)類型的變化[10].
內(nèi)核函數(shù)變化類型主要有: (1)函數(shù)被刪除或添加;(2)函數(shù)添加或刪除參數(shù), 即函數(shù)的參數(shù)個數(shù)發(fā)生變化;(3)函數(shù)參數(shù)類型改變; (4)函數(shù)返回類型變化, 如添加錯誤處理機制由void變?yōu)榉祷劐e誤碼.
內(nèi)核中宏的類型主要包括有參宏和無參宏. 無參宏的變化類型有: (1)宏被刪除或添加; (2)宏的值改變,即宏名沒有變化但是宏的值發(fā)生了變化, 它的調(diào)用方式不變, 但可能會影響使用宏值的變量或其它數(shù)據(jù)類型的結(jié)果. 對于有參宏來說, 變化類型同函數(shù)類似, 有參宏可以說是某種意義上的另一種形式的函數(shù).
數(shù)據(jù)類型主要包括結(jié)構(gòu)體, 聯(lián)合體和枚舉, 結(jié)構(gòu)體和聯(lián)合體的變化主要有: (1)刪除或者重新命名結(jié)構(gòu)體成員或結(jié)構(gòu)體; (2)更改結(jié)構(gòu)體成員類型; (3)更改結(jié)構(gòu)體成員順序; (4)增加或刪除結(jié)構(gòu)體別名. 枚舉類型的變化一般就是枚舉成員的增減或者整個枚舉結(jié)構(gòu)被刪除或添加.
接口變化還包括接口類型的改變. 例如接口在A版內(nèi)核中聲明為函數(shù), 在B版內(nèi)核中可能會更改為宏, 或者反之. 接口類型的改變對于設(shè)備驅(qū)動來說, 有的影響不大, 有的卻必須跟隨內(nèi)核接口的變化提供相應(yīng)的改變. 本文根據(jù)對設(shè)備驅(qū)動更新操作產(chǎn)生的影響將差異分析結(jié)果進行等級劃分, 再根據(jù)等級劃分對用戶做出不同的輔助更新提示信息的輸出. 設(shè)備驅(qū)動的自動更新就是將引言中圖1所示的研究過程, 最大化地實現(xiàn)自動化, 研發(fā)出一個插件或者獨立的小工具嵌入到編輯器或者大型集成開發(fā)工具中幫助內(nèi)核開發(fā)人員或者驅(qū)動維護人員更新相關(guān)程序的源代碼.
設(shè)備驅(qū)動自動更新的方法設(shè)計主體是考慮從驅(qū)動源代碼調(diào)用的內(nèi)核接口入手, 有針對性的分析驅(qū)動源代碼接口依賴, 根據(jù)其調(diào)用的依賴接口差異分析, 提示用戶做出什么樣的協(xié)同演化操作.
設(shè)備驅(qū)動自動更新的方法設(shè)計具體是: 首先加載編譯器插件編譯內(nèi)核導(dǎo)出特定格式的編譯中間結(jié)果,分析編譯中間結(jié)果提取待處理設(shè)備驅(qū)動程序源碼的接口依賴關(guān)系; 然后分析驅(qū)動源碼提取待處理驅(qū)動程序所依賴的頭文件, 根據(jù)頭文件列表提取兩版本內(nèi)核接口原型聲明信息, 進而根據(jù)提取的接口原型聲明信息獲取兩版本內(nèi)核中的接口差異信息, 并將差異信息篩選分類; 在此基礎(chǔ)上, 根據(jù)提取的接口依賴關(guān)系獲取設(shè)備驅(qū)動程序依賴接口差異信息及分類規(guī)則得出驅(qū)動更新的輔助信息, 最終向用戶展示結(jié)果.
圖3為設(shè)備驅(qū)動更新模型模塊設(shè)計, 主要包括預(yù)處理模塊, 設(shè)備驅(qū)動接口依賴關(guān)系提取模塊, 設(shè)備驅(qū)動頭文件依賴列表提取模塊, 驅(qū)動依賴接口原型聲明信息提取模塊, 驅(qū)動依賴接口差異分析和驅(qū)動更新輔助信息生成模塊. 設(shè)備驅(qū)動更新模型每個模塊既可以單獨運行也可以作為集成工具一起按規(guī)則自動執(zhí)行, 自動執(zhí)行腳本可以將各個模塊集成為一組工具集.
圖3 設(shè)備驅(qū)動更新模型
預(yù)處理模塊. 此模塊主要是編譯A版Linux內(nèi)核源碼獲取編譯中間結(jié)果. GCC編譯器自4.5.0版本后開始支持插件技術(shù)[11], 為用戶提供了豐富的API接口, 方便用戶獲取GCC編譯中間結(jié)果, 因此可通過GCC Plugin技術(shù)獲取編譯過程中的抽象語法樹然后導(dǎo)出特定格式的文件, 以便提取設(shè)備驅(qū)動程序接口依賴關(guān)系.
驅(qū)動接口依賴關(guān)系提取模塊. 根據(jù)C語言的語法規(guī)則, 接口應(yīng)該包括函數(shù)、宏、結(jié)構(gòu)體、枚舉結(jié)構(gòu)等主要方面. 此模塊就是分析預(yù)處理模塊得到的編譯中間結(jié)果即序列化的抽象語法樹文件, 提取A版內(nèi)核中設(shè)備驅(qū)動的內(nèi)核接口依賴關(guān)系, 生成包括設(shè)備驅(qū)動的自身接口聲明和去掉自身接口調(diào)用關(guān)系的外部接口調(diào)用關(guān)系文件, 調(diào)用關(guān)系包括驅(qū)動所調(diào)用的函數(shù), 引用的宏, 使用的數(shù)據(jù)類型, 還有這些接口的調(diào)用位置和主調(diào)函數(shù).
驅(qū)動頭文件依賴提取模塊. 提取A版內(nèi)核中待處理驅(qū)動程序源碼所直接引用的頭文件, 然后根據(jù)此頭文件信息進一步提取A、B兩版本內(nèi)核中頭文件中引用的頭文件, 稱之為二級頭文件即間接引用的頭文件,然后由二級頭文件以同樣的方法再提取三級頭文件,生成設(shè)備驅(qū)動對兩個內(nèi)核版本的頭文件依賴列表, 這個頭文件依賴列表基本包含了驅(qū)動程序依賴接口所涉及的頭文件. 而為了新增功能或者代碼重構(gòu)而添加頭文件導(dǎo)致一級頭文件列表的變化情況, 由于添加的頭文件對此時的用戶來說是透明的, 所以這種情況不予考慮.
接口原型聲明信息提取模塊. 此模塊是獲取驅(qū)動頭文件依賴提取模塊生成的兩個內(nèi)核版本的頭文件依賴列表中所有頭文件的內(nèi)容, 包括宏、函數(shù)以及extern原型的聲明, 結(jié)構(gòu)體、枚舉等數(shù)據(jù)類型的聲明,聲明信息包括接口名稱、接口類型, 所在文件, 起始行號, 原型聲明語句. 此模塊生成兩個版本內(nèi)核頭文件中的接口原型聲明文件.
接口差異分析和驅(qū)動更新輔助信息生成模塊. 此模塊是分析這些依賴關(guān)系在兩版內(nèi)核中的差異信息,例如函數(shù)調(diào)用, 這些差異信息就包括函數(shù)名稱、返回值的改變, 參數(shù)列表的改變, 如參數(shù)個數(shù)或者參數(shù)類型發(fā)生變化等. 再如宏常量的使用, 大多數(shù)宏常量名稱沒有變化, 但是其值卻發(fā)生了變化, 雖然值發(fā)生了變化但由于調(diào)用宏時用的是名字, 所以宏常量的值變化不影響編譯卻可能會影響后續(xù)的使用結(jié)果. 因此, 所有這些差異信息都要提取出來, 并加以分析處理, 然后根據(jù)對驅(qū)動更新操作的影響進行等級劃分, 最后把差異信息的分級處理結(jié)果轉(zhuǎn)化為驅(qū)動更新的輔助信息.
根據(jù)設(shè)備驅(qū)動自動更新方法設(shè)計, 設(shè)計并實現(xiàn)設(shè)備驅(qū)動更新輔助工具DDAU, 開發(fā)平臺是虛擬機32位操作系統(tǒng) Ubuntu 14.04 LTS, GCC 編譯器版本 4.8.4,Linux內(nèi)核版本3.5.6和3.8.13.
設(shè)備驅(qū)動更新輔助工具DDAU操作主要步驟包括: (1)預(yù)處理, 加載GCC插件編譯A版Linux內(nèi)核源碼得到編譯中間結(jié)果--序列化的抽象語法樹; (2)分析編譯中間結(jié)果提取待處理設(shè)備驅(qū)動程序源碼的接口依賴關(guān)系; (3)分析源碼提取待處理設(shè)備驅(qū)動程序所依賴的頭文件列表, 包含兩個內(nèi)核版本頭文件依賴列表;(4)根據(jù)頭文件列表提取兩版本內(nèi)核頭文件的接口的原型聲明信息; (5)根據(jù)提取的接口依賴關(guān)系和接口的原型聲明信息獲取接口差異信息, 再由差異信息得出驅(qū)動更新的輔助信息, 然后向用戶展示結(jié)果等5個主要步驟. 各步驟之間以及生成的文件的關(guān)系如圖4所示.
圖4 DDAU各步驟和生成文件之間的關(guān)系圖
預(yù)處理步驟主要是編譯內(nèi)核執(zhí)行make時設(shè)置參數(shù) EXTRA_CFLAGS=“-fplugin=plugin_name”加載GCC插件導(dǎo)出序列化的抽象語法樹文件, 頭文件依賴分析步驟是得到驅(qū)動兩版內(nèi)核頭文件依賴列表, 現(xiàn)在著重介紹驅(qū)動接口依賴分析、接口原型聲明提取、依賴接口差異分析和驅(qū)動更新輔助信息生成的設(shè)計與實現(xiàn).
預(yù)處理階段導(dǎo)出的序列化抽象語法樹文件, 是以函數(shù)為單位構(gòu)成, 樹的根節(jié)點是函數(shù)聲明節(jié)點func_decl, 它的子樹有返回值子樹result_decl, 參數(shù)子樹parm_decl, 以及它的函數(shù)體子樹 bind_expr, 根據(jù)不同子樹節(jié)點不同含義可以提取驅(qū)動自定義函數(shù)聲明信息和函數(shù)、宏調(diào)用關(guān)系信息以及數(shù)據(jù)結(jié)構(gòu)的使用信息等.
以函數(shù)聲明節(jié)點為入口和界限, 提取驅(qū)動自身定義的接口, 提取函數(shù)體子樹bind_expr中驅(qū)動調(diào)用的函數(shù)、宏, 以及結(jié)構(gòu)體等, 對于函數(shù)、宏的調(diào)用需要記錄下所有主調(diào)函數(shù)和調(diào)用位置. 函數(shù)調(diào)用信息還需要剔除設(shè)備驅(qū)動程序調(diào)用自身定義函數(shù)的調(diào)用信息. 最后將這些函數(shù)定義以及函數(shù)、宏等的調(diào)用關(guān)系即驅(qū)動接口依賴關(guān)系分別保存成文本文件供后續(xù)處理.
根據(jù)頭文件列表獲取模塊, 可以分析這些頭文件的內(nèi)容, 得到設(shè)備驅(qū)動程序依賴的頭文件中聲明的函數(shù)、宏、結(jié)構(gòu)體等的原型聲明原句, 以便在接口差異信息獲取和驅(qū)動更新輔助信息生成模塊比較差異和根據(jù)差異生成驅(qū)動更新輔助信息.
本文使用Exuberant-Ctags[12](以下統(tǒng)稱為Ctags)作為信息獲取的輔助工具. 首先修正Ctags的輸出. 由于Ctags的主要目的是用于生成索引, 它并不關(guān)心接口是如何聲明的, Ctags生成各種索引的時候, 雖然列出了索引的文件名和行號, 但是在取原型聲明語句的時候只是取了對應(yīng)行號的那一行, 因此它的輸出結(jié)果并不能滿足實驗需求, 需要對Ctags的輸出結(jié)果中接口聲明部分進行修改, 將函數(shù)、宏的原型聲明提取完整, 對于數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu), 輸出的結(jié)果中數(shù)據(jù)的名字和數(shù)據(jù)成員是拆分開的, 需要重新合并組合成一個完整的數(shù)據(jù)類型或數(shù)據(jù)結(jié)構(gòu)的聲明, 以滿足實驗中對接口原型聲明信息的提取和后續(xù)的差異分析.
接口差異類型如圖5所示.
圖5 接口差異類型分類
用v0表示原版內(nèi)核接口信息, v1表示新版內(nèi)核接口信息, 接口信息存儲在Python語言的字典dict中,用k表示接口名稱, file表示接口所在文件, type表示接口類型即接口是函數(shù)還是宏或者是結(jié)構(gòu)體等等,state表示接口原型聲明語句, diff_type表示差類型,#號表示注釋語句, 差異分析算法關(guān)鍵代碼如下:
上述操作后剩下的v1_dict中的接口就是Add增加類型的接口.
根據(jù)在接口依賴關(guān)系分析模塊得到的接口調(diào)用關(guān)系信息, 可以從接口差異信息中提取設(shè)備驅(qū)動所調(diào)用的接口是否發(fā)生了變化, 以及發(fā)生了哪些變化. 利用差異篩選算法把這些差異信息又根據(jù)對設(shè)備驅(qū)動程序更新的影響程度分為表1所示4個等級.
表1 差異信息影響等級劃分
輔助更新信息只輸出A、B、C三個等級.
首先進行模塊化黑盒測試, 分析各個模塊得出的結(jié)果是否正確, 確認(rèn)各個模塊之間銜接正確, 再對輔助工具進行整體測試, 查看最后得出的更新輔助信息, 然后根據(jù)輔助信息介入人工操作對驅(qū)動進行更新, 最后將更新的驅(qū)動復(fù)制到B版內(nèi)核(假設(shè)此操作之前B版內(nèi)核還沒有更新對應(yīng)驅(qū)動)的對應(yīng)位置, 對B版內(nèi)核進行編譯, 測試是否編譯通過, 編譯通過后加載運行測試驅(qū)動模塊進一步驗證.
選擇測試驅(qū)動時, 為了盡可能測試多種驅(qū)動類型,選擇Linux內(nèi)核源碼中drivers目錄下不同的驅(qū)動目錄進行實驗測試驗證, 修改配置文件中內(nèi)核源代碼所在路徑, 然后運行批處理自動執(zhí)行腳本文件batch_run.sh,也可以運行單處理腳本run.sh分析單個測試文件.
對Linux內(nèi)核源碼中drivers目錄下不同驅(qū)動目錄進行實驗測試驗證, 統(tǒng)計分析結(jié)果時不是單純統(tǒng)計代碼行數(shù), 而是統(tǒng)計設(shè)備驅(qū)動依賴的內(nèi)核接口變化而應(yīng)當(dāng)采取的設(shè)備驅(qū)動更新操作, 設(shè)備驅(qū)動更新操作修改的可能是一行代碼, 也可能是相關(guān)聯(lián)的多行代碼, 不考慮源代碼內(nèi)部優(yōu)化問題.
為量化實驗結(jié)果, 本文提出漏報率和誤報率概念.漏報率 (Rate of Missing Report, RMR)是指驅(qū)動程序根據(jù)設(shè)備驅(qū)動依賴接口變化應(yīng)當(dāng)給出但未給出修正提示的次數(shù)(MR)占據(jù)根據(jù)設(shè)備驅(qū)動依賴接口變化應(yīng)當(dāng)給出修正提示的總次數(shù)(TAR) 的比例. 誤報率(Rate of False Report, RFR)是指驅(qū)動程序根據(jù)設(shè)備驅(qū)動依賴接口變化所給出的錯誤修正提示次數(shù)(FR)占據(jù)根據(jù)設(shè)備驅(qū)動依賴接口變化給出修正提示的總次數(shù)(TR)的比例. 單個設(shè)備驅(qū)動文件的漏報率(RMRi)的計算公式是 RMRi= MRi/TARi. 平均漏報率的計算公式是 RMR= ∑RMRi/N,N為文件總數(shù), 平均漏報率的數(shù)值越低越好. 單個設(shè)備驅(qū)動文件的誤報率(RFRi)的計算公式是RFRi= FRi/TRi. 平均誤報率的計算公式是 RFR =∑RFRi/N,N為文件總數(shù), 平均誤報率的數(shù)值越低越好.實驗結(jié)果統(tǒng)計如表2所示.
表2 drivers/目錄測試文件的測試結(jié)果統(tǒng)計表
對于漏報原因分析: (1)接口在A版內(nèi)核中定義在驅(qū)動內(nèi)部, 而B版內(nèi)核中把定義移到了頭文件中, 并可能更改接口名, 例如pci-acpi.c驅(qū)動中接口add_pm_notifier(參數(shù))為自定義接口, 在B版內(nèi)核則把此接口移動到了acpi/acpi_bus.h頭文件里并改名為acpi_add_pm_notifier(參數(shù)), 而驅(qū)動程序中的調(diào)用則同步替換.(2)內(nèi)核頭文件中既保留原有接口函數(shù)、又新增同功能接口函數(shù)、并調(diào)用新增同功能接口函數(shù)的情況. 這兩種漏報情況, 前者需要進一步優(yōu)化接口差異分析算法, 后者無法避免.
對于誤報原因分析: (1)函數(shù)參數(shù)只名字改變, 函數(shù)原型差異分析考慮了參數(shù)名, 造成誤報. (2)函數(shù)返回值發(fā)生變化, 但調(diào)用時沒有使用函數(shù)返回值, 因而函數(shù)返回值改變并沒有對程序造成影響, 無需給出更新提示, 造成誤報. (3)可變參數(shù)宏變化成了固定參數(shù)宏,但由于調(diào)用時參數(shù)個數(shù)依然可以接受, 所以無需修正,卻給出修正提示, 造成誤報. (4) 宏變化成函數(shù), 返回值、參數(shù)由無類型變更為具體類型, 例如兩版本接口聲明語句: “-- #define con_debug_enter(vc) 函數(shù)體 ++static inline int con_debug_enter(struct vc_data *vc)”, 此情況給出更新提示誤報可以諒解. (5)對于函數(shù)參數(shù)中某個參數(shù)類型和名稱均發(fā)生變化, 例如內(nèi)核接口async_synchronize_full_domain , 參數(shù)列表由 (struct list_head*list)變?yōu)?(struct async_domain *domain), 但驅(qū)動程序源碼中此接口調(diào)用語句參數(shù)部分卻都是 (&scsi_sd_probe_domain), 調(diào)用不受影響, 無需給出更新提示, 此情況的誤報可以諒解.
對于誤報原因各種情況分析得出, 實際平均誤報率要比計算得出的誤報率小的多, 因而DDAU可以為驅(qū)動更新提供有效的輔助更新提示.
輔助更新提示信息中的A級差異信息生成的輔助信息如圖6所示, 依次是接口差異信息的級別A級, 接口的差異類型 Del, 接口類型, 接口所在文件, 接口的原型聲明信息, 這些信息方便用戶了解接口的調(diào)用方式,接下來是驅(qū)動文件調(diào)用此接口的位置, 直接定位源代碼方便用戶修改. 圖7中所示是接口的差異類型All,輔助信息會把接口變化前后的文件和原型聲明列出來,方便用戶查看以便根據(jù)需要修改代碼.
根據(jù)輔助信息的提示, 對A版驅(qū)動進行人工輔助更新操作, 根據(jù)輔助信息提供的接口變化類型和接口原型信息進行修改, 將修改好的驅(qū)動放到B版內(nèi)核對應(yīng)位置后編譯內(nèi)核, 經(jīng)驗證可以編譯通過, 證明論文的設(shè)備驅(qū)動輔助更新方法設(shè)計是可行的.
圖6 設(shè)備驅(qū)動更新A級輔助信息部分結(jié)果示例
圖7 設(shè)備驅(qū)動更新B級輔助信息部分結(jié)果示例
文獻(xiàn)[3,4]利用GCC插件導(dǎo)出已知兩版內(nèi)核設(shè)備驅(qū)動源碼的抽象語法樹文件, 提取接口名字和在抽象語法樹文件中的起止行號, 傳入修改了前后端的Gumtree[13]進行差異分析, 分析驅(qū)動內(nèi)部代碼實現(xiàn)的細(xì)節(jié)差異. 但由于其是基于已知兩版驅(qū)動代碼比較, 且旨在比較接口實現(xiàn)內(nèi)部細(xì)節(jié), 因而并不能實現(xiàn)驅(qū)動的更新操作. 與之相比, DDAU是利用GCC插件分析待更新的驅(qū)動代碼提取待更新設(shè)備驅(qū)動接口依賴, 同時依據(jù)引用的頭文件分析依賴接口差異, 并最終得出輔助更新提示, 在此基礎(chǔ)上, 就可以根據(jù)提示對原有設(shè)備驅(qū)動源碼進行修正而得到新版驅(qū)動源碼.
直接編譯方法[14]是在新版內(nèi)核中編譯驅(qū)動源碼,修改錯誤, 重新編譯, 不斷重復(fù)此過程直到驅(qū)動編譯不再出錯, 但改錯的方法是人工分析提取的錯誤日志, 這需要人工查找接口在兩版內(nèi)核中的接口原型聲明信息,對比接口原型聲明差異分析調(diào)用如何更改驅(qū)動源碼,與直接編譯方法不同, DDAU為用戶提示的驅(qū)動更新輔助信息, 直接提供了接口的變化類型, 接口所在文件和兩版接口原型聲明對比, 以及驅(qū)動代碼調(diào)用此接口的位置, 直觀的為用戶展示出接口聲明的不同和代碼需要修改的位置, 為驅(qū)動開發(fā)和維護人員省去大量繁瑣的人工查找和定位修改代碼的時間, 大大減少了驅(qū)動更新操作所耗費的時間.
本文實現(xiàn)了一個Linux設(shè)備驅(qū)動程序自動更新輔助工具DDAU, 以便用戶解決隨著內(nèi)核更新帶來的驅(qū)動協(xié)同演化問題. 該工具可以為內(nèi)核驅(qū)動的開發(fā)和維護提供有效的輔助更新信息, 為驅(qū)動在內(nèi)核空間和用戶空間的拆分自動化問題[15]提供借鑒意見, 同時也為操作系統(tǒng)軟件更新問題提供技術(shù)思路. 但此工具也存在一些不足之處: (1)由于使用GCC插件所以對于驅(qū)動內(nèi)核中的條件編譯語句#ifdef—#endif括起來的非本機器體系架構(gòu)的代碼無法處理; (2)對于接口發(fā)生變化,但依據(jù)調(diào)用情況的不同需要做出不同提示的情況不夠靈活進行處理, 需要進一步的完善; (3)接口差異分析算法對處理接口名稱改變但功能不變的特殊情況需要進一步細(xì)化完善; (4)對于驅(qū)動接口依賴分析中的數(shù)據(jù)類型或數(shù)據(jù)結(jié)構(gòu)依賴關(guān)系的提取不夠全面; (5)更新輔助信息提示不能圖形化動態(tài)展示在源碼需要更新的位置, 沒有將此輔助工具嵌入vi等編輯器或者集成開發(fā)環(huán)境中進而高效率更新代碼. 這些不足之處需要進一步的研究和探索, 會不斷的修正和完善.