讀鴻蒙論文,看效能最佳化

泊浮目發表於2024-11-04
版本日期備註
1.02024.11.4文章首發

本文內容已用一種抽象的方式做成了影片,喜歡看影片的同學可以在B站上搜尋“泊浮目”觀看相應的內容。

簡介

Microkernel Goes General: Performance and Compatibility in the HongMeng Production Microkernel》論文中提到的一些效能最佳化的思路和方法是很有學習價值的,結合論文裡提到的點,我做了個影片。為了方便大家觀看,我梳理了這個文字版本。

讀前提示:Linux是個通用作業系統,鴻蒙是特殊領域專用系統。在專屬領域中,鴻蒙肯定會比Linux發揮得好。作為開發者,我們需要知道軟體工程中的trade off,才不會被一些標題黨帶著走。

為何造輪子

大家都知道Linux是個成熟的作業系統,那為什麼還要重新造個鴻蒙出來呢?主要有兩個原因:

  1. 在嵌入式、安全要求極高的場景(比如軍工),需要利用微核心來構築核心。減少核心中的功能,將不重要的程式隔離出核心,這樣會讓核心更安全、可靠。
  2. Linux是面向通用發展的,在一些特定領域(除了上述提到的,還有汽車、移動裝置)去做領域適配並不合適。比如 ‘PREEMPT_RT’(Real-Time Linux) 這個補丁前後花了10年才正式合入Linux。

那麼什麼是微核心呢?簡單來說就是核心只保留最基本的能力,比如程序排程、虛擬機器記憶體、中斷等,把一些應用放到了使用者空間,比如驅動程式、檔案系統等。這樣系統服務與系統服務之間是隔離的,單個服務出現故障或者完全攻擊,也不會導致整個作業系統掛掉,提高了作業系統的穩定性和可靠性。

論文中舉了一個例子:Linux的程式碼中,驅動和檔案繫系統有3000多w行,佔到了程式碼庫的80%,過去4年的1000個通用漏洞披露中90%來自於這部分的程式碼。

但是微核心會有效能問題。以檔案系統為例,和硬體裝置互動就需要頻繁切換到核心態,這樣會帶來效能損耗。那鴻蒙怎麼解決這類問題呢?我們就來解讀解讀論文裡提到的最佳化點。

最佳化1:Synchronous RPC-like IPC Fastpath

中文可以翻譯成 同步RPC樣式的IPC快速通道 吧。

這個章節裡提到了IPC(程序間)的通訊在鴻蒙的場景中很頻繁。在過程中很容易消耗大量的記憶體,為啥消耗記憶體?舉個最簡單的例子,AB之間程序之間通訊,中間是不是有個通道,要不要耗記憶體?程序之間通訊的時候,不能把主程序做的事阻塞住吧,那是不是要起執行緒去做,是不是要一些棧記憶體?

那鴻蒙是怎麼最佳化呢?針對上述的棧,鴻蒙搞了個棧池,程序服務能在裡面複用就複用,不夠的時候就擴,當然它也會記錄每個程序擴的量,擴多了肯定不給擴了,不然記憶體爆了。

可能有同學好奇,什麼叫Fastpath,我們以RPC FastPath為例,看下常見的最佳化:

  1. 減少上下文切換

    • 直接呼叫:對於一些高頻呼叫的服務,可以將遠端呼叫轉換為本地直接呼叫,避免網路傳輸和上下文切換的開銷。
    • 內聯呼叫:在某些情況下,可以直接內聯遠端服務的邏輯到呼叫方,減少遠端呼叫的開銷。
  2. 快取機制

    • 結果快取:對於重複呼叫且結果不變的服務,可以快取結果,避免重複呼叫。
    • 狀態快取:快取一些狀態資訊,減少每次呼叫時的狀態查詢開銷。
  3. 零複製技術

    • 共享記憶體:使用共享記憶體來傳遞資料,避免資料從一個程序複製到另一個程序。
    • 直接I/O:使用直接I/O(如DMA,Direct Memory Access)技術,減少資料在使用者態和核心態之間的複製。
  4. 協議最佳化

    • 緊湊的訊息格式:使用緊湊的訊息格式(如Protocol Buffers、FlatBuffers)來減少訊息體積。
    • 壓縮演算法:對訊息進行壓縮,減少傳輸的資料量。
  5. 多路複用

    • 連線複用:保持長連線,避免頻繁建立和關閉連線的開銷。
    • 批次處理:將多個RPC請求打包成一個批次進行處理,減少網路往返次數。
  6. 非同步處理

    • 非同步IO:使用非同步IO機制,減少等待時間。
    • 事件驅動:採用事件驅動模型,提高併發處理能力。
  7. 預連線機制:預先建立好連線,減少每次呼叫時的握手時間和開銷。
  8. 智慧路由

    • 負載均衡:透過負載均衡器智慧分配請求,避免單一節點成為瓶頸。
    • 就近接入:根據地理位置或網路拓撲選擇最優的接入點。
  9. 硬體加速

    • NIC解除安裝:使用支援解除安裝功能的網路卡(NIC),減輕CPU負擔。
    • FPGA/ASIC加速:使用專用硬體(如FPGA、ASIC)來加速資料處理。

最佳化2:隔離級別

第二個是關於程序隔離級別的,有點像在傳統的核心態和使用者態之間加了一箇中間態。

這一層適用於效能有要求而且已經驗證的作業系統服務,當然這一層如果被攻擊了,整個系統是要G的。

所以論文裡提到,如果出現了新的攻擊,鴻蒙是可以快速把IC1的應用退到IC2的。這也算是微核心帶來的一個好處吧。

  • IC0:IC0是核心部分,任何被攻破的IC0服務都可以任意讀取和修改其他服務的記憶體。因此,將服務放置在IC0級別應當經過仔細驗證,以避免核心核心被破壞。
  • IC1:適用於效能關鍵且已驗證的作業系統服務。鴻蒙將核心地址空間劃分為不同的域,併為每個服務分配一個獨特的域(IC0/核心核心也駐留在一個獨特的域中)。鴻蒙一些機制(ARM watchpoint and Intel PKS) 來防止跨域記憶體訪問。此外,由於IC1服務在核心空間執行,它們可以執行特權指令。為了避免這一風險,鴻蒙採用二進位制掃描和輕量級的方法來防止未經授權的特權指令執行。

    • 具體實現是IC1服務之間(或到IC0)的IPC會進入核心核心中的一個gate,gate執行最小的上下文切換(僅切換指令和棧指標,不包括地址空間切換和排程),並配置硬體以切換域(只需要幾個CPU週期)。這樣的門無法被繞過,因為域切換需要特權指令。因此,IPC的開銷被顯著減少。如圖4所示,與使用者空間服務(IC2IC2)相比,它減少了IC1服務之間IPC延遲的50%。
  • IC2:這個沒什麼特殊的。IC2適用於非效能關鍵的服務或包含第三方程式碼的服務(例如,Linux驅動程式),透過地址空間和特權隔離來強制實施

最佳化3:Flexible Composition

第三個叫Flexible Composition,則是參考單核心的最佳化方式。將緊密耦合的OS服務合併,以減少高效能需求場景中的IPC頻率(如下圖左側中間黃色部分,File System和Mem Mgr就合併了)。

system arti.png

最佳化4:Address Token-based Access Control

我翻譯成為基於地址令牌的訪問控制

這裡面提到呢,一般微核心會將核心物件隱藏在核心後,透過許可權控制訪問。如果要對核心物件進行訪問,就會涉及到核心態使用者態的切換而帶來的許可權問題。而正是由於微核心最小化原則,某些核心物件(例如頁表)的需要頻繁地由核心之外的作業系統服務進行更新。

那鴻蒙是怎麼最佳化的呢?每個核心物件被放置在HM的唯一物理頁面上,然後透過許可權配置只讀、讀寫來對映給作業系統服務,避免總是經過核心降低效能。

最佳化5:Policy-free Kernel Paging

這是一個關於Page fault的一個最佳化,那什麼是Page fault?這裡我們做一個簡單的計算機基礎知識回顧。

Page fault是指當程式嘗試訪問其虛擬地址空間中的一個頁面,而這個頁面並沒有載入到實體記憶體(RAM)中時所發生的情況。當發生 page fault 時,作業系統會介入處理,並將所需的頁面從磁碟上的交換檔案或其它儲存介質載入到實體記憶體中。

這個其實涉及到了計算機實現虛擬記憶體的關鍵機制之一,它們允許作業系統使用比實際實體記憶體更大的地址空間。在實際應用中呢,swap分割槽就是用來這個事情的。

那在鴻蒙的場景中呢。page fault會有一定的效能問題。主要原因在於從核心到分頁器的額外往返通訊。具體來說,在丟擲頁面錯誤異常後,核心會向分頁器發出一次程序間通訊(前面說到過這是一個OS服務),分頁器檢查地址並分配新的頁面,然後返回核心更新頁面表,最後回到應用程式。

總得看下來,異常處理是在微核心裡做的,但是決策是在核心之外做的。那麼鴻蒙做了一個改進,它在核心中保留了一個預設的頁面錯誤處理機制——鴻蒙在核心中有段匿名記憶體的地址範圍以及預先分配的物理頁面(這段記憶體都是高效能程式要求的區域),那對於這部分記憶體,核心知道它可以立即對映到實體記憶體上,而無需進一步的決策過程。後面非同步同步給記憶體管理器這個操作記錄就行了。

如果頁面錯誤發生在非效能關鍵區域(即不在記憶體管理器提供的匿名記憶體地址範圍內),或者預先分配的頁面已經被用盡,那麼核心將不得不傳送一個IPC請求給記憶體管理器來獲取新的物理頁面。這種情況下,記憶體管理器將根據其策略來分配新的物理頁面,並通知核心進行相應的頁面對映。

那總得來說呢,鴻蒙裡面提到的效能最佳化手段還是比較精彩的。大多數基於work load來做出最佳化的,思路值得學習。

作業系統上的生態該怎麼辦?

說完如何解決微核心的效能問題。我們繼續講解生態問題。

大家都知道寫個作業系統比較容易,網上教程一搜一大把,但是如何讓作業系統上的生態建立起來是個問題。

這裡面做了個Linux ABI的相容層,該層將所有Linux系統呼叫重定向到適當的OS服務。也就是說,其他Linux系統的二進位制程式,理論上是可以直接相容的。

而驅動這塊,出於安全性的考慮是放在IC2的。但論文裡面提到了一個有意思的方法,根據安全和效能要求,切分了驅動中的資料平面和控制平面。

對於驅動呢,鴻蒙會在IC1中另起一個程式,方便做隔離,兩個程式之間是透過IPC來通訊的,這兩個程式分別來做控制平面和資料平面分離:控制平面負責管理、配置,並在較高層次上做出決策的部分;「資料平面則處理實際資料傳輸和處理任務的部分;前者對安全性要求高、效能要求低,後者反之。HM把裝置驅動中「資料平面」(也就是IO操作)的部分,委託給IC1空間中的twin driver來完成,而控制平面操作留在LDC中。

小結

總得來說,鴻蒙作業系統的誕生還是沒有擺脫那句經典的話語——計算機世界總是在通用和專用之間搖擺。

當通用軟體沒法滿足需求,而需求越來越多的時候呢,則會出現對應的專用軟體。

相關文章