<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          LWN:以QEMU為例解析軟件復(fù)雜度!

          共 6574字,需瀏覽 14分鐘

           ·

          2021-10-24 14:03

          關(guān)注了就能看到更多這么棒的文章哦~

          A QEMU case study in grappling with software complexity

          October 12, 2021
          This article was contributed by Kashyap Chamarthy
          KVM Forum
          DeepL assisted translation
          https://lwn.net/Articles/872321/

          要制作出具有長(zhǎng)期可靠性以及可維護(hù)性的軟件,有很多障礙。其中之一就是軟件的復(fù)雜性。在最近結(jié)束的 2021 年 KVM 論壇上,Paolo Bonzini 以 open source 的 emulator 和 virtualizer (仿真器和虛擬化器) QEMU 為例,探討了這個(gè)話題。根據(jù)他作為 QEMU 中幾個(gè)子系統(tǒng)的維護(hù)者的經(jīng)驗(yàn),他針對(duì)如何抵御不該有的復(fù)雜性提出了一些具體建議。Bonzini 在整個(gè)演講中使用 QEMU 作為實(shí)例,希望讓未來(lái)的貢獻(xiàn)者更容易對(duì) QEMU 進(jìn)行修改。然而,他所分享的經(jīng)驗(yàn)也同樣適用于其他許多項(xiàng)目。

          為什么軟件的復(fù)雜性會(huì)成為一個(gè)問(wèn)題?首先,大家都知道它會(huì)導(dǎo)致各種錯(cuò)誤,也會(huì)引入安全缺陷(security flaws)。對(duì)于復(fù)雜的軟件來(lái)說(shuō),對(duì)代碼進(jìn)行 review 會(huì)更加困難;它也使得希望對(duì)項(xiàng)目進(jìn)行貢獻(xiàn)和維護(hù)的時(shí)候感到更加麻煩。顯然,這些都是缺點(diǎn)。

          Bonzini 希望能回答一個(gè)問(wèn)題,那就是 "我們能在多大程度上消除復(fù)雜性?"。為此,他首先把復(fù)雜性分為了 "基本的(essential) "和 "偶然的(accidental)" 復(fù)雜性兩種。這兩類(lèi)復(fù)雜性的概念源于 1987 年 Fred Brooks 的經(jīng)典論文《No Silver Bullet》。Brooks 本人則是在借鑒了亞里士多德的 essence and accident 的定義。

          正如 Bonzini 所說(shuō),essential complexity 是 "軟件程序試圖解決的問(wèn)題本身固有的屬性"。而 accidental complexity 則是 "正在解決手頭問(wèn)題的過(guò)程相關(guān)的一個(gè)特性"(即這不是由我們想要解決的問(wèn)題本身所具有的困難)。為了進(jìn)一步解釋這些概念,他挑選了一些在 QEMU 中正在解決的問(wèn)題,用來(lái)說(shuō)明 QEMU 的 essential complexity。

          Essence and accidents of QEMU

          QEMU 在可移植性、可配置性、性能和安全性方面有許多要求。除了要模擬(emulate)guest device,以及提供保存和恢復(fù) guest 狀態(tài)的方法之外,它還有一個(gè)功能強(qiáng)大的 storage layer,并且還帶有一些網(wǎng)絡(luò)服務(wù)功能,如 VNC server。QEMU 還必須確保暴露給客戶的 CPU 和 device 的模型保持穩(wěn)定,哪怕曾經(jīng)更新過(guò)所運(yùn)行的硬件環(huán)境或 QEMU 本身。許多用戶來(lái)說(shuō)都希望在 QEMU 上直接使用發(fā)行版的內(nèi)核,而不希望專(zhuān)門(mén)修改一個(gè)內(nèi)核來(lái)使用。對(duì)于許多 QEMU 用戶來(lái)說(shuō),能夠啟動(dòng)非 Linux 的操作系統(tǒng)也是一個(gè)必備功能,這個(gè)也算是 essential complexity。

          QEMU 提供了一個(gè)管理界面,通常稱為 monitor。實(shí)際上是有兩個(gè),分別是 HMP(人類(lèi)監(jiān)控協(xié)議,human monitor protocol)和 QMP(QEMU monitor protocol),因?yàn)橛脩粢残枰粋€(gè)簡(jiǎn)單的方法來(lái)與 monitor 互動(dòng),而不希望使用 QMP 提供出來(lái)的基于 JSON 的接口,外部程序則要 QMP 的接口來(lái)來(lái)管理 QEMU。因此,QEMU 包含了一個(gè)對(duì)象模型(object model)和一個(gè)代碼生成器(code generator),它可以處理 C structure 的雙向轉(zhuǎn)換(marshaling and unmarshaling)。得益于這個(gè)代碼生成器,可以簡(jiǎn)單地使用同樣的代碼來(lái)相應(yīng) JSON 或命令行參數(shù)的操作方式。

          開(kāi)發(fā)人員還看到了另一方面的復(fù)雜性,這是由于 build 過(guò)程中的相關(guān)工具所引入的。工具一般會(huì)使常見(jiàn)的任務(wù)變得更容易,但它們也使得調(diào)試工作在發(fā)生故障時(shí)變得更加困難。例如,QEMU 曾經(jīng)有一個(gè)手動(dòng)配置機(jī)制,需要用戶逐一列舉出它所要模擬的電路板上的所有設(shè)備。現(xiàn)在,只需要指定某個(gè)電路板,build 系統(tǒng)就會(huì)自動(dòng)把它所支持的設(shè)備都啟用起來(lái)。它還能確保不會(huì)去 build 那些不合理的配置,這個(gè)機(jī)制也非常有用。但是,開(kāi)發(fā)者仍然必須要學(xué)會(huì)如何處理這類(lèi)出錯(cuò)情況。

          Sources of complexity

          在演講中,Bonzini 介紹了 accidental complexity 的兩個(gè)主要來(lái)源。第一個(gè)是 "incomplete transitions"(受到一篇關(guān)于 GCC 維護(hù)的論文的啟發(fā)),也就是當(dāng)一種新的、更好的方法被引入的時(shí)候,這個(gè)新方法尚未在整個(gè)代碼庫(kù)中統(tǒng)一應(yīng)用起來(lái)。這可能是由于多種原因造成的:開(kāi)發(fā)人員可能沒(méi)有時(shí)間或缺乏相關(guān)的專(zhuān)業(yè)知識(shí),或者他們根本沒(méi)有找到那些被遺漏的情況。

          他列舉了在 QEMU 中報(bào)錯(cuò)的兩種截然不同的方式作為例子:一種是 propagation-based API,另一種是將 error 直接寫(xiě)入標(biāo)準(zhǔn)輸出的具體函數(shù)(例如 error_report())。propagation-based API 是為了向 QMP 接口報(bào)告錯(cuò)誤而引入的。它有兩個(gè)優(yōu)點(diǎn):它將錯(cuò)誤發(fā)生的位置與報(bào)告的位置分離開(kāi),并可以比較干凈地進(jìn)行 error recovery。另一個(gè) incomplete transitions 的例子是,盡管現(xiàn)在 QEMU 的 build 系統(tǒng)主要使用 Meson,但仍有一些之前就存在的 build test 是用 Bourne shell 編寫(xiě)的,仍然是 QEMU 的配置腳本的一部分。

          然而,QEMU 歷史上也有一些徹底完成切換(transition)的例子。其中有幾次是使用 Coccinelle 來(lái)完成的(這是一個(gè)模式匹配和源碼轉(zhuǎn)換工具,允許創(chuàng)建一個(gè) "semantic patch,語(yǔ)義補(bǔ)丁",用來(lái)在整個(gè)代碼庫(kù)中修改所有相關(guān)點(diǎn)。例如,曾經(jīng)使用 Coccinelle 來(lái)替換那些過(guò)時(shí)了的 API、 簡(jiǎn)化一些不必要的中間調(diào)用、甚至引入全新的 API(如 device 的創(chuàng)建和 "realization")。

          accidental complexity 的第二個(gè)來(lái)源是重復(fù)邏輯以及缺乏抽象。在編寫(xiě)臨時(shí)代碼和設(shè)計(jì)可重復(fù)使用的數(shù)據(jù)結(jié)構(gòu)和 API 之間,有一個(gè)平衡點(diǎn)需要權(quán)衡。Bonzini 舉了命令行解析的例子,有些臨時(shí)代碼在使用 strtol() 或 scanf() 等函數(shù),相應(yīng)地正規(guī)代碼使用的是 QEMU 專(zhuān)用 API(如 QemuOpts 或 keyval)。后者確保了命令行的一致性,有時(shí)還能協(xié)助打印一些 help 信息。

          另一個(gè)例子是最近發(fā)生的,人們?cè)谠噲D將 QEMU 的更多部分組織成可以獨(dú)立安裝的 shared object。隨著這類(lèi) module 的數(shù)量增加,我們就建立了一個(gè)新機(jī)制,可以將一個(gè) module 所提供的功能和它的依賴關(guān)系列在實(shí)現(xiàn)相關(guān)功能的源文件代碼中,而不是讓它們散落在 QEMU 源代碼各處。Bonzini 建議,一旦 reviewer 看到有過(guò)多的重復(fù)內(nèi)容,或者某個(gè)功能散亂分布在許多文件中,他們就應(yīng)該計(jì)劃一下如何消除這種情況。

          Complexity on the QEMU command line

          講座接著介紹了一個(gè)關(guān)于 QEMU accidental complexity 的案例研究,即命令行的處理代碼。QEMU 有 117 個(gè)選項(xiàng),用了大約 3000 行的代碼來(lái)實(shí)現(xiàn),這里有 "一些 essential complexity,但有太多 accidental complexity"。Bonzini 簡(jiǎn)要介紹了一些可以對(duì)這里進(jìn)行簡(jiǎn)化處理的方法,也就是說(shuō)在處理 QEMU 命令行解析代碼時(shí)如何使其至少不會(huì)變得更糟。他首先問(wèn)道:到底是什么導(dǎo)致了 QEMU 命令行選項(xiàng)的這些 accidental complexity?這么多的選項(xiàng)參數(shù),具體的實(shí)現(xiàn)都有很大的差異,所以講座將它們歸為了六類(lèi),并按照 accidental complexity 的遞增順序進(jìn)行了逐項(xiàng)介紹:flexible (靈活性)、command (命令)、combo (組合)、shortcut (快捷方式)、one-off (一次性),以及 legacy (遺留內(nèi)容)。

          flexible option 是最復(fù)雜的,因?yàn)樗鼈冇脕?lái)滿足廣泛的需求。它引入了 QEMU 中大多數(shù)的 essential complexity,QEMU 的新功能通常是通過(guò)這些 option 來(lái)啟用的。flexible option 的工作機(jī)制是將盡可能多的功能轉(zhuǎn)化給通用的 QEMU APIs 來(lái)執(zhí)行,因此啟用新功能通常不需要對(duì)命令行解析代碼進(jìn)行任何新增或者修改。這就是為什么可以用一個(gè)單個(gè)選項(xiàng) -object 來(lái)完成配置諸如加密密鑰、TLS 證書(shū)、虛擬機(jī)與 host 上的 NUMA 節(jié)點(diǎn)的關(guān)聯(lián)等的原因。而使用了三個(gè)選項(xiàng),分別是 -cpu、-device 和 -machine 來(lái)配置了虛擬硬件的幾乎所有特性。然而,這些 option 也不可避免地會(huì)有 accidental complexity:至少使用了四個(gè) parser 來(lái)解析這些 option。這四個(gè)分別是 QemuOpts、keyval、一個(gè) JSON 解析器,以及一個(gè)被 -cpu 選項(xiàng)所使用的專(zhuān)門(mén)定制的解析器。"四個(gè)解析器至少有兩個(gè)是可以不需要的。"

          command 選項(xiàng)是在 QEMU 命令行上指定的,但它通常也對(duì)應(yīng)于在 runtime 調(diào)用的某個(gè) QMP 命令。舉例來(lái)說(shuō),guest 啟動(dòng)時(shí)可以不讓 vCPU 運(yùn)行(qemu-kvm -S 命令行命令,或者也可以在 runtime 停止),而后續(xù)需要時(shí)再啟動(dòng) CPU(通過(guò) QMP cont,表示 "繼續(xù)")。另外的例子有 -loadvm,用來(lái)從一個(gè)保存有 guest 狀態(tài)的文件中啟動(dòng) QEMU。還有 trace,是用來(lái)啟用 trace point 的(這要求 QEMU 在 build 的時(shí)候帶上了相應(yīng)的某個(gè) tracing 后端支持)。這些 option 給 QEMU 的維護(hù)者帶來(lái)的負(fù)擔(dān)相對(duì)比較小,但 Bonzini 建議在添加新的命令行選項(xiàng)時(shí)保持一個(gè)較高的標(biāo)準(zhǔn),這樣才能今后從 QMP 接口設(shè)置這些 option 是更加容易。

          加上了 combo 選項(xiàng)之后,"我們開(kāi)始進(jìn)入 accidental complexity 的地獄":這些選項(xiàng)在一個(gè)命令行選項(xiàng)中同時(shí)創(chuàng)建了 device 的前端和后端。例如,QEMU 的 -drive 選項(xiàng)會(huì)創(chuàng)建一個(gè)類(lèi)似 virtio-blk 這樣的 device,以及一個(gè) guest 的磁盤(pán)鏡像。這個(gè)選項(xiàng)還有一些更加細(xì)化的變體,它們對(duì)于普通用戶來(lái)說(shuō)已經(jīng)很不方便了,所以 combo 選項(xiàng)確實(shí)是真正有用的,但維護(hù)這些選項(xiàng)給大家?guī)?lái)的負(fù)擔(dān)也很重。相關(guān)的解析代碼是很復(fù)雜的,而且這些選項(xiàng)也往往會(huì)對(duì)代碼的其他部分產(chǎn)生影響,包括 backend 代碼以及 virtual-chipset 的 creation 相關(guān)代碼。這些選項(xiàng)使得 QEMU 的代碼變得不那么模塊化,也因?yàn)檫@個(gè)原因,如果不了解命令行的細(xì)節(jié)的話,就無(wú)法增加對(duì)新 board 的支持。

          shortcut 是前三組選項(xiàng)的語(yǔ)法糖(syntactic sugar)。例如,-kernel path 是 -machine pc,kernel=path 的縮寫(xiě)。它們很方便(許多用戶可能甚至沒(méi)有意識(shí)到較長(zhǎng)形式的存在)而且相應(yīng)的維護(hù)負(fù)擔(dān)很小,因?yàn)樗鼈兊膶?shí)現(xiàn)完全可以依賴現(xiàn)有的命令行解析代碼來(lái)完成。然而,考慮到已經(jīng)存在這么多的選項(xiàng)了,所以最好也不要再增加。

          然后是 one-off 選項(xiàng)。這些選項(xiàng)是必不可少的,但它們的實(shí)現(xiàn)往往是不夠優(yōu)化的。它們經(jīng)常會(huì)向全局變量寫(xiě)入一個(gè)數(shù)值,或者調(diào)用一個(gè)無(wú)法在 runtime 通過(guò) QEMU monitor 調(diào)用的函數(shù)。Bonzini 懇請(qǐng)開(kāi)發(fā)人員務(wù)必避免創(chuàng)建再新的命令了,而是對(duì)現(xiàn)有的命令進(jìn)行重構(gòu),改為 shortcut 或 command 選項(xiàng),他在過(guò)去一年中一直在斷斷續(xù)續(xù)地做這件事。

          最后,來(lái)到了 legacy 命令行選項(xiàng),"我們跌到了谷底"。其中許多都是來(lái)自失敗的實(shí)驗(yàn)(例如 -readconfig 和-writeconfig 選項(xiàng)),或者根本就不應(yīng)該出現(xiàn)在 QEMU 中的東西。例如,-daemonize 在初始化后讓 QEMU 進(jìn)程變成守護(hù)進(jìn)程,而 libvirt 等工具給用戶提供的方案更加好。這些工具的未來(lái),就是等待被廢棄(deprecate)并最終刪除掉。

          Ways to fight back

          QEMU 命令行帶來(lái)了哪些教訓(xùn),以及開(kāi)發(fā)者可以從中學(xué)到哪些原則?他說(shuō):"不要從無(wú)到有地進(jìn)行設(shè)計(jì)",要利用現(xiàn)有的 essential complexity。在著手添加一個(gè)新的命令行 flag 之前,問(wèn)問(wèn)自己是否有必要。也許可以跟 QEMU 中的 QEMU API 和 QMP 命令等集成起來(lái)。這樣一來(lái),就可以充分利用 QEMU 的子系統(tǒng)之間現(xiàn)有的互相配合的機(jī)制。

          其次,Bonzini 強(qiáng)調(diào)了 patch reviewer 的責(zé)任:需要理解復(fù)雜性中的 essential 的這部分,不要把它誤認(rèn)為是 accidental。只有這樣做了,才能真正發(fā)現(xiàn) accidental complexity 是否在上升。而且不要讓 accidental complexity 完全占據(jù)我們的項(xiàng)目。對(duì)于那些從事大型代碼庫(kù)重構(gòu)的人,他鼓勵(lì)先學(xué)習(xí)一下 Coccinelle。

          incomplete transition 并不總是可怕的事情:從一個(gè)舊 API 過(guò)渡到一個(gè)新的、更好的 API 是軟件改進(jìn)中自然會(huì)發(fā)生的事情。就 QEMU 而言,有時(shí)一個(gè)新功能無(wú)論如何都需要一個(gè) transition 階段,因?yàn)樗鼤?huì)影響到命令行或管理工具,因此就需要一個(gè) deprecate 周期。在這種情況下,利用 incomplete transition,分階段來(lái)完成工作。辨別出所有進(jìn)行的工作中最小的各個(gè)任務(wù),并為先做哪些后做哪些做好計(jì)劃。

          此外,需要確保這些新的、大家推薦的完成開(kāi)發(fā)任務(wù)的方法、或使用某個(gè)功能的最佳方法要被得到相應(yīng)的文檔記錄。"希望在做任何任務(wù)的時(shí)候都能有一個(gè)很明顯正確的方法完成。如果沒(méi)有這么明顯的方法的話,那就要有一個(gè)記錄下來(lái)的方法。" incomplete transition 或者分段過(guò)渡(piece-wise transition)都不應(yīng)該阻礙人們對(duì)程序進(jìn)行改進(jìn)。需要對(duì)重復(fù)代碼和添加更多抽象層之間進(jìn)行權(quán)衡。有些情況可能需要重復(fù)代碼,但當(dāng)情況變差的時(shí)候,不要再繼續(xù)讓它繼續(xù)惡化了。

          Conclusion

          要搭建 essentially-complex 并且可維護(hù)的(maintainable)的軟件已經(jīng)很困難了。如果這里討論的 accidental complexity 的因素(incomplete transition、過(guò)度抽象、組件之間不明確的邏輯邊界和工具的復(fù)雜性)沒(méi)有得到控制,那么隨著時(shí)間的推移就會(huì)出現(xiàn)越來(lái)越復(fù)雜的情況。從 QEMU 的經(jīng)驗(yàn)中提煉出來(lái)的教訓(xùn)為其他面臨類(lèi)似麻煩的項(xiàng)目提供了不少借鑒。

          [我想感謝 Paolo Bonzini 對(duì)本文早期草稿的深入 review。]

          全文完
          LWN 文章遵循 CC BY-SA 4.0 許可協(xié)議。

          歡迎分享、轉(zhuǎn)載及基于現(xiàn)有協(xié)議再創(chuàng)作~

          長(zhǎng)按下面二維碼關(guān)注,關(guān)注 LWN 深度文章以及開(kāi)源社區(qū)的各種新近言論~



          瀏覽 59
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲欧美日韩久久精品第一区 | 婷婷五月天激情小说视频 | 国产91精品在线观看 | 综合操逼网 | 欧美成人免费观看 |