[轉帖]知乎 微內核

做者:韓樸宇
連接:https://www.zhihu.com/question/339638625/answer/784544135
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

早期的計算機,好比運行DOS的IBM PC或者早期的大型機IBM 709, 是沒有內核這種概念的, 從軟盤、磁帶或者紙帶加載程序以後程序就能夠徹底的控制整個計算機硬件.linux

後來隨着歷史的發展,出現了一種需求:多我的同時訪問一臺計算機。爲了知足這種需求,出現了現代化的操做系統:分時操做系統。早期流行而且至今產生深遠影響的分時操做系統就是Unixgit

這裏有幾個概念:github

  1. 分時:因爲CPU只有一個(當時沒有多核心CPU),可使全部進程輪流執行一個很短的時間,使用戶以爲本身的程序一直在執行。爲了實現此功能,操做系統必須具備調度進程,管理進程的功能,此外CPU必須定時的執行操做系統的調度功能。進程調度是內核最重要的功能。
  2. 中斷:CPU在收到硬件(好比時鐘,觸控屏觸摸,鼠標點擊)的通知時會暫停執行當前的程序, 跳轉到對應的中斷處理程序上,而這個程序是內核的一部分。執行部分指令也會產生中斷,根據產生的緣由又分爲異常(程序遇到問題不能繼續執行)和陷阱(程序主動的發起)。中斷處理程序的入口,存儲在被稱爲中斷向量表的數據結構中。管理硬件資源和異常也是內核很重要的功能。
  3. 內存保護:CPU執行的指令,讀取的數據都來自內存,是全部硬件資源中最重要的。爲了「同時」運行多個程序,同時阻止惡意或者故障的程序讀取、修改其餘進程的內存,CPU和操做系統具備限制內存訪問的功能。爲了實現這一點,CPU提供了虛擬內存的功能,全部指令只能訪問虛擬的內存地址,由CPU的內存管理單元(MMU)將其轉換爲實際的內存地址,操做系統須要管理一種叫頁表的數據結構,記錄了映射規則和讀、寫、執行的權限。全部進程都有本身獨有的頁表,其中只包含此進程能訪問的內存地址,訪問不存在的地址時就會報錯(x86上即通用保護異常).
試圖寫入不能寫的地址,Windows就直接關閉了此程序

微軟也編寫了關於虛擬內存的文檔: 虛擬地址空間 - Windows driverswindows

4. 特權級與系統調用:許多指令,好比設置頁表或者中斷向量表的地址,修改CPU的工做模式,都屬於特權指令,普通程序禁止執行, CPU使用特權級(又稱RING或者異常級別)表示當前正在執行的進程的權限.低權限的程序執行高權限的指令或者訪問高權限的內存會觸發異常.瀏覽器

這是AARCH64(arm64)的特權級(Exception level)描述圖

權限級最低的,就是普通的用戶態進程(EL0) , 最高的,就是操做系統內核(EL1)安全

其實上圖有4層結構,EL2用於虛擬機管理程序,而虛擬機能夠執行多個操做系統。(實際上手機不使用此功能),最高的EL3就是CPU的固件。而右側的部分爲可信計算環境,處理諸如加密,指紋等機密信息. 據華爲PPT,當前鴻蒙微內核實際上運行於此(不是電視上那個鴻蒙OS).網絡

爲了容許用戶態進程訪問高權限資源,操做系統內核提供了經過陷阱調用系統功能的入口,即系統調用. 打開文件(open)寫文件(write)都是系統調用。數據結構

能夠看到,用戶態程序和內核之間存在自然的界限.架構

須要注意的一點是,操做系統會附帶一些重要的系統進程,好比Linux的init, 可是這些也只是用戶態程序, 操做系統內核也許對其有特殊的標記,可是對於CPU來講和其餘進程沒有區別.併發


下面討論一下Windows,比較使用量很廣.

上圖中,Windows的內核主要部分即ntoskrnl.exe,也稱內核映像. 處於商業的考慮,Windows實際上有4種不一樣的內核映像.而smss.exe則是第一個用戶態程序.

Windows的內核代號爲NT, 其架構圖爲

這個圖很複雜,咱們也沒必要搞懂...紫色的內核模式和藍色的用戶模式區分很明顯.

其中一個很小的kernel mode drivers(內核模式驅動程序),看起來不起眼,其實是NT內核中體積最大的部分.

諸多微軟官方和第三方的驅動程序

數不勝數的硬件,經過這種方式被Windows兼容,經過Windows update和即插即用(PnP)機制,作到了最佳的體驗。

Windows是一個閉源操做系統,也基本上無論驅動的編寫(除去CPU和那些已經標準化的協議)。硬件製造商(或者毒瘤軟件)想要編寫驅動程序,須要安裝Visual studio,Windows SDK和HDK(硬件開發工具),打開MSDN(如今更名了),照着文檔裏的函數接口寫。

使用C/C++編寫Windows驅動

編寫完的驅動包括.sys驅動程序和inf描述文件,交給微軟進行兼容性測試,隨後就能得到簽名併發布了。微軟同時保證,這些接口不會發生變化,即保證了二進制兼容性(編譯好的驅動即便系統更新也不會出現問題).

固然也有例外(逃

上圖都是驅動的問題....

前面提到了虛擬內存的概念,Windows大多數驅動都是內核模式驅動程序(.sys),少數是用戶模式驅動程序(.dll), 微軟在這裏提到了區別: 用戶模式和內核模式 - Windows drivers

運行 Windows 的計算機中的處理器有兩個不一樣模式:用戶模式 和內核模式 。 根據處理器上運行的代碼的類型,處理器在兩個模式之間切換。 應用程序在用戶模式下運行,核心操做系統組件在內核模式下運行。 雖然許多驅動程序之內核模式運行,但某些驅動程序可能以用戶模式運行。
啓動用戶模式應用程序時,Windows 會爲該應用程序建立進程 。 進程爲應用程序提供專用的「虛擬地址空間」 和專用的「句柄表」 。 因爲應用程序的虛擬地址空間爲專用空間,所以一個應用程序沒法更改屬於其餘應用程序的數據。 每一個應用程序都隔離運行,若是一個應用程序發生故障,則故障僅侷限於該應用程序。 其餘應用程序和操做系統不會受該故障的影響。
除了專用以外,用戶模式應用程序的虛擬地址空間也受到限制。 在用戶模式下運行的處理器沒法訪問爲操做系統保留的虛擬地址。 限制用戶模式應用程序的虛擬地址空間可防止應用程序更改以及可能損壞關鍵的操做系統數據。
在內核模式下運行的全部代碼都共享單個虛擬地址空間。 這意味着內核模式驅動程序不會與其餘驅動程序和操做系統自己隔離。 若是內核模式驅動程序意外寫入錯誤的虛擬地址,則屬於操做系統或其餘驅動程序的數據可能會受到安全威脅。 若是內核模式驅動程序發生故障,整個操做系統就會發生故障。

所謂的發生故障,就是藍屏. 因爲顯卡驅動故障的機率很高, 使用用戶模式驅動程序會減小藍屏, 微軟也使用特殊的Windows顯示驅動框架(WDDM)強制要求顯卡驅動部分實如今用戶模式下. 在Windows10中, Win+Ctrl+Shift+B快捷鍵便可重啓顯卡驅動和圖像堆棧, 而放在XP的時代,顯卡驅動出現故障就會直接藍屏.

正是由於Windows支持用戶模式驅動程序,所以Windows屬於混合內核.


咱們再討論一下GNU/Linux這邊的狀況, Linux僅僅是操做系統的內核, Busybox組成的initrd, Systemd, GNU libC,Xorg等等用戶態進程組成了一個系統.

Linux的「官方網站」爲The Linux Kernel Archives

Github上有Linus Torvalds先生維護的Linux分支的鏡像torvalds/linux

廣告一下我維護的Linux 0.11的代碼, 能夠方便的在現代系統上研究12101111/Linux-0.11

Linux是一個Unix-like(即沒有Unix商標,可是兼容Unix的POSIX API)的內核, 當年仍是大學生的Linus買到了一臺低配版386兼容機,不想用落後的DOS系統,而想用Unix.但當時的Unix是一款昂貴的商業產品.

當時計算機教學使用的系統叫Minix,是Andrew Stuart "Andy" Tanenbaum編寫的一個流行微內核Unix-like系統,這個系統隨者做者的書《操做系統:設計與實現》分發,可是不是自由軟件.

當時另外一個流行的Intel 386平臺的Unix系統爲FreeBSD,不過當時此項目正在和AT&T公司(即Unix全部者)打官司, 並且不兼容沒有387協處理器的機器, 所以Linus本身照着Andy的課本和Intel手冊寫出了Linux, 使用GPLv2協議成爲自由軟件, 並經過互聯網開始協做開發.

Linus還就微內核和宏內核的問題和Andy 論戰過: 塔能鮑姆-託瓦茲辯論

Linux不只如標準的宏內核同樣全部功能位於內核空間中, 並且因爲GPLv2, 全部驅動程序的源代碼(理論上)都須要開源, 所以Linux不多有下載驅動這一操做, 由於全部驅動都是開源的, Linux源碼裏已經有了.後來Linux具備了模塊系統,驅動能夠按需加載, 不須要爲了刪除不須要的驅動而從新編譯內核了.

和Windows不一樣的是, Linux的驅動接口常常變更, 所以即便編譯好的內核模塊(.ko文件)常常升級Linux版本就不能用了, 所以驅動只能隨着主線更新, 若是沒有人維護就會從主線中剔除.

部分arm芯片廠商的內核版本就很老,就是由於它們既不想把源代碼交給社區維護,也追不上社區升級版本號的速度.

因爲Linux並無用戶模式驅動的功能,所以編寫差勁的開源nVidia驅動(雖然開發者已經很努力了)和一樣編寫差勁卻又不開源的nVidia閉源驅動常常形成kernel panic(等價於Windows藍屏)


那麼微內核究竟是什麼一種東西呢.

微內核將傳統宏內核中的驅動程序,甚至包括許多功能,好比文件系統, 網絡, GUI等等, 都變成用戶態的進程(服務),而內核中只保留最重要的功能: 進程管理,內存管理, 進程間通訊, 以及硬件抽象層(HAL).

而Linux, 即便是驅動源碼獨立於內核倉庫,具備明顯的模塊化特徵,具備穩定的驅動接口,也不能是微內核,惟一的標準就是用戶態的驅動程序和系統服務

傳統Unix內核(左)和微內核(右) 假如把NT改造爲微內核,那麼至少須要從內核中刪除內核模式驅動程序和GDI/WindowManager

Google的Fuchsia OS的 Zircon內核也是微內核, 詳見: 聞其詳:許中興博士演講:Fuchsia OS 簡介完整實錄及幻燈片下載

Redox, Rust編寫的微內核操做系統, 能夠看到各類驅動的服務進程

從理論上將, 微內核將驅動獨立於內核空間, 大大下降了驅動故障的風險, 提高了安全性, 熱更新驅動熱重啓驅動也變得可能.

微內核的代碼大大的簡化(少去多半的驅動代碼), 維護成本降低, 只要提供一個合理的驅動接口便可.

但有一個重要的問題: 這樣作的效率會下降.

從開銷上計算, 函數調用<<系統調用<進程間通訊(IPC)

函數調用(同一特權級): 壓棧,跳轉目標地址,彈棧, 返回棧中的返回地址. 函數調用能很好的被CPU的流水線感知.

系統調用(不一樣特權級): 清空流水線,TLB刷新,保留現場(用戶態程序各個寄存器的值)可能的進程調度,轉向執行其餘進程,複製用戶空間裏的數據到內核空間(字符串等系統調用參數),處理,將結果複製回用戶空間,恢復現場,清空流水線,TLB刷新

進程間通訊: 屢次系統調用,可能須要屢次複製消息體,可能發生進程調度。

最大的問題,就在IPC的性能上.

根據目前的硬件,系統調用時,若是要傳遞寄存器存不下的東西,好比內存中的一段字符串,那麼內核是不能直接讀取用戶態的內存的(頁表不一樣),內核須要先把用戶提供的內存地址翻譯到內核中的地址,而後內核分配一塊內核使用的內存,將其複製進去,而後處理. 返回數據也相似.

再好比內核處理網卡的數據,並將此數據傳給瀏覽器.

宏內核的過程: 網卡發來中斷, CPU執行網卡驅動設置的處理程序,要求網卡使用DMA將數據存到內核中,而後返回.DMA完成後,再次中斷,系統的IP子系統分析已經存儲的IP數據報,肯定是TCP數據,根據端口號喚醒正在等待recv()的瀏覽器,將數據複製到recv參數裏的緩衝區. 整個過程只發生一次數據的複製,兩次中斷.

那麼微內核怎麼辦呢, 這裏以最傻的微內核爲例: 網卡發來中斷, CPU執行HAL設置的處理程序, HAL肯定這個中斷應該被/bin/e1000d這個進程處理,因而喚醒/bin/e1000d, 把網卡發來的中斷信息發給/bin/e1000d, e1000d將「寫網卡內存映射的寄存器.....要求網卡進行DMA..."這種消息發給內核,內核收到以後執行,而後DMA結束以後把內存數據複製給e1000d,e1000d處理以後使用IPC把IP數據報發送給/bin/ipd,ipd處理後把TCP數據發送給瀏覽器。

實際上發生了屢次數據的複製,頁表的修改,屢次進程調度,效率遠低於宏內核。

經過簡化代碼,雖然能提升IPC的性能,但遠不如函數調用快。實際上上述例子中TCP/IP協議棧即便是微內核也不該該放在用戶態中。

而進一步的提速,可能須要對現有的內存保護模型進行修改,避免過多的複製, 而硬件的修改,則是比軟件的革新難數倍.

相關文章
相關標籤/搜索