x86仮想化技術のメモ

カーネル/VM探検隊などに触発されたので、私もVM関連の情報をまとめていく。こうかと思ったが、ザーと書いているうちに飽きてきたので途中で終わってます(笑)
ググればわかるような情報を書き出すのに時間を使うよりも、実際にソース読んでいったほうが面白いなって思ってしまったので。

目次

  • 仮想化技術はどのように使われているか
  • 仮想化技術を実装してみるには何をする必要があるか、その手法の概要
    • CPUの仮想化
    • メモリの仮想化  ←ここまでで終わり(笑)
    • I/Oの仮想化

一応、以上について書き進めていたんだけど、正直「A Comparison of Software and Hardware Techniques for x86 Virtualization」を読めばかなりの情報が得られます。そんなわけでどうぞー。間違ってるところは、指摘してもらえるとうれしいです。

仮想化技術はどのように使われているか

サーバー統合
e.g.:VMware ESX/ESXi, Xen, Hyper-V
開発支援
e.g.:VMware Workstation, QEMU
セキュリティの確保
e.g.:BitVisor

いろいろな用途があるけど総論として、仮想化技術はハードウェア層と従来のソフトウェア層の間に1枚、仮想化の層を加えることで、これまで出来なかったことを出来るようにする。その代わり、仮想化の層のために、性能、セキュリティ、運用管理性の低下、ライセンス費用の増加などのデメリットを被ることになる。

そんなデメリットがあっても、日本でも数年前からメリットのほうが大きいと判断されるようになり、特に企業向けの「サーバー統合技術」として利用されるようになっている。メリットが大きいと判断されるようになった理由は、仮想化技術の熟成による仮想化ソフトウェアの品質向上、ハードウェアの能力向上による性能劣化の相対的縮小などで、日本市場においては導入実績の拡大もとても大きい。

仮想マシンVM)を作るには何をする必要があるのか

冒頭で列記した仮想化ソフトウェアのようにVMを提供するには、ハードウェア資源をVM上のソフトウェア(ゲスト)から適切に隠ぺい(仮想化)しないといけない。複数のVMが動作する場合は、VMが相互に影響を与えないようにすべきだし、そのような仮想化処理を行う仮想化ソフトウェア(VMM:仮想マシンモニタ)もVMから隠ぺいされることが望ましい。
これらを行うために、主に以下の処理が必要となる。

  1. CPUの仮想化/Instruction set virtualization
  2. メモリの仮想化/Memory virtualization
  3. I/Oの仮想化/I/O virtualization

各項目についてみていく。

CPUの仮想化

なぜ必要になるのか

命令の中には、そのまま実行するとVMMやほかのVMに影響を及ぼすものがあるため、これを安全に処理する必要がある。このような命令のことを「センシティブ(sensitive)命令」といい、センシティブ命令を適切に処理しなくてはならない。

どのようなアプローチがあるか

センシティブ命令を処理するためにはいくつかの方法がある。


1.特権レベル降格による命令トラップとエミュレーション/Trap-and-emulate
ゲストカーネルの特権レベルを0から1へ降格させて実行することで、ゲスト内で特権命令が発行された場合に例外が発生し、制御がVMMに移るようにする。VMMでは特権命令をエミュレートし、ゲストカーネルへ結果と制御を返す。
特権レベルを降格させることから"Ring Compression"あるいは"De-Privileging"とも呼ばれる。

Ring Native De-Privileged
3 User User
2 - -
1 - Kernel
0 Kernel VMM

この方法は、すべてのセンシティブ命令が特権命令でなければならない*1(特権命令でない場合、特権レベルが降格されていてもトラップが発生しないため)。しかし、x86では18種類の非特権センシティブ命令が存在するため、このアプローチは利用できない。
参考:ITpro:x86で簡単に仮想化ができないのはなぜ?


2.準仮想化/Paravirtualization
あらかじめ静的にゲストカーネルを変更し、センシティブ命令を正しく処理できるようにしておく。
どの程度ゲストを変更するかによって準仮想化でできることは変わってくるが、代表例XenではゲストカーネルをDe-Privileging(Ring-1化)することでゲストからXen本体(Ring-0)を保護し、センシティブ命令はHypercallと呼ばれるXen機能の呼び出しに置き換えることで安全に処理できるようにする。

準仮想化は名前の通り厳密には仮想化といえる技術ではなく、CPUを仮想化するためだけの技法でもない。準仮想化技術を利用して、後述するメモリの仮想化やI/Oの仮想化も支援することができる。


3.バイナリ変換/Binary Translation(BT)
実行時にゲストのバイナリコードを解析、変更し、安全な命令にする技法。
ゲストの命令を都度VMMのインタプリタ(変換機)を通して解析し、センシティブ命令であれば安全な命令に変換してエミュレートする。解析と変換には高いコストがかかるため、変換回数を極力抑えるために技巧が凝らされるのが普通(複数の命令を一括返還する、変換結果をキャッシュする、変換しない/Direct Execution、など)。

この方法は非常に汎用性が高い方法でゲストへの変更を一切必要としない。VMware社が得意とする手法で、10年以上のノウハウを持っている。


4.ハードウェア機能の利用/Intel VT-x / AMD-V
Intel VT-x / AMD-Vというプロセッサ組み込みのCPU仮想化支援機能を使う。
この機能を使うと、ゲストに例外、割り込みが発生したときや、特定の命令を実行しようとしたときに自動的にVMMへトラップされるようになる。VMMはトラップされた理由を調べ、適切に処理してゲストへ制御を返すことで、センシティブ命令を安全に処理できるようになる。

AMD-Vのことはよく調べていないので、これ以降はIntel VT-xに話題を限定する。

Intel VT-xを有効にすると、従来のリングプロテクションとは別のモード「VMX operation モード」になる。VMX operation モードは、ゲストを動作させるための「VMX non-root モード」と、VMMを動作させるための「VMX root モード」に分けられる。

ゲストが(VMX non-root モードで)センシティブ命令などを実行しようとすると、自動的に例外VM-exitが発生し、VMX root モードとなりVMMへと制御が移る。VMMはVM-exitした理由を調べ、適切に命令を処理したのちにゲストへ制御を戻す(VM-entry)ことでゲストのセンシティブ命令が処理される。

この手法は、ゲストへの変更を一切必要としない点と、従来捕捉が難しかったセンシティブ命令を自動的にトラップするためVMMの構造・実装が非常に容易になるという点で優れている。一方、ハードウェアをある程度限定してしまうことや、ゲストがリアルモードで動作する場合などはVMX operation モードがカバーできないという欠点もある。

なお、速度的な優位性があるわけではないことに注意が必要。性能はハードウェアに依存するため、特に古い世代のプロセッサの場合はVM-exit/entryのコストが高くつく。VMMの機能レベルに合わせてVM-exitの発生を抑制することはできるが、VMware製品で比較すると、多くの場合ごく最近のプロセッサまでBT技術をデフォルトとして採用している。

参考:ESX Monitor Modes
参考:Performance aspects of x86 virtualization

CPU仮想化における各手法のまとめ

XenVMwareなどは複数の手段を適宜組み合わせて利用し、単純構造を良しとするKVMなどはIntel VT-x / AMD-Vのみを採用している。

Trap-and-emulate

  • 非特権センシティブ命令が存在すると利用できない
  • VMMの実装は比較的容易
  • 特権命令のたびにトラップされるため性能が低下しやすい

Paravirtualization

  • ゲストの修正が必要(ソースコードが必要)
  • VMMの実装は比較的容易
  • 単純なTrap-and-emulateやBinary Translationより最適化されているため性能を稼ぎやすい

Binary Translation

  • ゲストの修正が不要
  • VMMの実装は複雑
  • 変換処理は複雑であるため性能が低下しやすい

Intel VT-x / AMD-V

  • ゲストの修正が不要
  • VMMの実装は比較的容易
  • 性能はプロセッサの世代次第
  • ゲストがプロテクトモードの場合しか利用できない
  • ハードウェアを限定する

メモリの仮想化

なぜ必要になるのか

カーネルは物理メモリを占有できているものとして設計されているが、VMで動作する場合にはそのようにはできない(実際にはメモリ仮想化の仕組みを実装しない選択肢もあるが、その場合、別個のVMといえる状態ではないし、VMMが保護されないため危険でもある)。

問題点は、仮想メモリアドレス(VA)から物理メモリアドレス(PA)へのアドレス変換で、ゲストVAからホストPAを得るための処理を実装しなければならない。このアドレス変換はMMU(Memory Management Unit)というハードウェアが行うため「MMUの仮想化」と表現することも多い。

どのようなアプローチがあるか

1.ソフトウェア的な対応/Shadow Page Table
ゲストがアドレス変換を行おうとしたときに発生するページフォルトをVMMにトラップし、VMMは実CR3に登録した自前のゲストVA→ホストPAの変換テーブル(Shadow Page Table)を参照することでホストPAの解決を行う。Shadow Page Tableはソフトウェア的に実装し、維持管理する必要があり、単純な実装方法では、コンテキストスイッチでCR3が変更されるたびにShadow Page Tableを作りなおさなくてはならないため、実行時コストが非常に大きくなる。
また、ゲストで発生したページフォルトをすべてハンドリングする必要があったり、ゲストカーネルはページテーブルに直接書き込みを行うため、これ捕捉してShadow Page Tableの側も同期させる必要があるなど、実装も複雑になりやすい。


2.ハードウェア機能の利用/Extended Page Table(EPT) / Rapid Virtualization Indexing(RVI)
名前の通り、従来の変換テーブル(PDPT, PD, PT)に加えて新しいテーブルを持つ。このテーブルはゲストVA→ホストPAの対応表として機能する。ゲストはこれを利用し、以下の手順でアドレス変換を行う。

  1. 仮想CR3をもとにゲストVA→ゲストPAを変換
  2. EPTポインタをもとにゲストPA→ホストPAを変換
  3. ゲストVA→ホストPAをTLBにキャッシュ

通常より変換ステップが多いため、TLBミスが発生すると性能に影響が出やすい。
なお、Microsoftの文書ではSecond-Level Address Translation (SLAT) と表現される。

*1:このような前提を満たす命令セットを「仮想化可能/virtualizable」と表現する。