虛擬化與容器

陷阱與模擬(trap-and-emulate)

這是「安全地執行客體作業系統」最古老、也最優雅的點子。你讓客體的無害指令以全速直接跑在真 CPU 上——加數字、搬資料、跑迴圈。但你安排好:客體一旦試圖做某件特權的事——碰真硬體、改分頁表、關閉中斷——CPU 就自動把它攔下、把控制權交給超管理器。超管理器接著在虛擬硬體上「假裝」做了那件特權的事,再讓客體繼續跑。客體永遠不會察覺它被攔截了。

這個機制就是陷阱(trap,也叫 fault 或 exception)。超管理器讓客體跑在一個低的、非特權的層級。當客體核心——它相信自己有特權——執行一條特權指令時,CPU 看到一個非特權程式企圖做特權的事,便引發一個陷阱,跳到超管理器事先安裝好的程式碼。超管理器解碼出客體本來想跑哪條指令,針對這台虛擬機的狀態執行等效的操作(例如,在一個虛擬旗標暫存器裡記下客體「關閉了中斷」,而不是真的去關閉),然後跳回客體、就落在那條惹事指令的後面。能跑就放手快跑,直到碰到危險的東西,那就陷入;模擬;繼續。

美妙之處在於:常見情形——普通運算——不必多花一毛,只有那些罕見的特權操作才付出陷阱與模擬的代價。麻煩之處,也是個著名的麻煩,是這套機制只有在「每一條表現得像特權的指令,在非特權下執行時都確實會陷入」時才成立。經典的 x86 架構打破了這條規則:某些敏感指令在非特權下既不陷入、卻又默默地什麼都不做或回傳錯誤資訊,於是一個純 trap-and-emulate 的超管理器逮不到它們。正是這一個缺陷,使得早期的 x86 虛擬化需要二進位翻譯或半虛擬化,也使得硬體廠商最終加入了 VT-x 與 AMD-V。

客體核心執行 x86 指令 cli(「清除中斷旗標」,意思是「關閉中斷」)。在一台可虛擬化的機器上,這會陷入超管理器,後者在一個虛擬旗標暫存器裡記下「客體想關中斷」並繼續執行——真正的中斷仍維持開啟,好讓主機保有控制權。

一條特權的客體指令陷入;超管理器在虛擬狀態上假造它的效果,再繼續執行。

陷阱與模擬只有在「每一條敏感指令於非特權下執行時都會陷入」時才成立。經典 x86 存在敏感卻不陷入的指令,所以純 trap-and-emulate 無法虛擬化它——這正是 Popek-Goldberg 故事的核心。

又称
trap-and-emulate virtualizationclassical virtualization陷入並模擬