KVMはなぜネイティブに近いのか
一言でいうと
仮想化の性能は、ゲストがカーネルモードに抜け出す回数(VM Exit)で決まります。ハードウェア支援とvirtioは、どちらもその回数を減らす技術です。
なぜ必要なのか
x86は、もともと仮想化に向かないアーキテクチャでした。特権命令のうち一部に、非特権モードで実行しても例外を起こさず、静かに別の値を返すものがあったからです(いわゆる「仮想化不可能命令」)。そのため、初期の仮想化は2つの方向に分かれました。
- 完全仮想化(Full virtualization): ゲストコードを実行の直前に検査・書き換えします(バイナリ変換)。ゲストOSを直す必要はありませんが、遅いです。
- 準仮想化(Paravirtualization): ゲストOSを直して、特権命令の代わりにハイパーバイザーを直接呼び出す(hypercall)ようにします。速いですが、ゲストを修正する必要があります。
ここにIntel VT-x / AMD-Vが登場して、状況が変わりました。CPUにゲスト専用の実行モードを追加したのです。
どう動くのか
ハードウェア支援仮想化
VT-xは、CPUに2つの新しいモードを作ります。VMX root(ハイパーバイザー)とVMX non-root(ゲスト)です。ゲストは自分のリング0でそのまま動きますが、ハイパーバイザーが決めた特定の事象が起きると、自動的にrootモードに抜け出します。これがVM Exitです。
メモリもハードウェアが助けます。EPT(Intel) / NPT(AMD)は、ゲスト物理アドレス → ホスト物理アドレスの変換を、ハードウェアのページテーブルで処理します。これがなかった時代には、ハイパーバイザーがシャドウページテーブルをソフトウェアで維持する必要があり、それが性能の大きな部分を食っていました。
KVMの位置づけ
KVMは、別のハイパーバイザーではなく、Linuxカーネルモジュールです。ロードされた瞬間に、Linuxカーネル自体がハイパーバイザーになります。そのため、KVMは、タイプ1(ベアメタル)とタイプ2(ホスト型)の境界にあります。ホストOSの上で動きますが、そのホストOSがそのままハイパーバイザーだからです。
実行の流れは次のとおりです。
QEMU (유저 공간)
├─ ioctl(KVM_RUN)
↓
KVM 모듈 (커널)
├─ VMLAUNCH / VMRESUME
↓
게스트 (VMX non-root)
├─ VM Exit 발생
↓
KVM 이 처리 가능하면 여기서 끝 (빠름)
KVM 이 못 하면 QEMU 로 반환 (느림)
このコードブロックの韓国語は、上から順に、QEMU(ユーザー空間)、KVMモジュール(カーネル)、ゲスト(VMX non-root)、VM Exitの発生、KVMが処理できればそこで終わり(高速)、KVMができなければQEMUに戻す(低速)、という意味です。
KVMがカーネルで直接処理するもの: ほとんどのMSRアクセス、単純なI/Oポート、EPT違反、外部割り込み。 QEMUまで上がるもの: 複雑なデバイスI/O、MMIO、一部のCPUID。
そのため、性能チューニングの方向が決まります。QEMUまで上がるExitを減らします。
QEMUの役割
QEMUはデバイスモデルです。ゲストが見る仮想NIC、ディスクコントローラー、タイマーをソフトウェアで作ってくれます。KVMなしでQEMUだけを使うと、CPU命令まですべてソフトウェアで変換しますが、そのエンジンがTCG(Tiny Code Generator)です。
TCGは、ゲスト命令を基本ブロック単位で切ってTCG IRに変え、さらにホスト命令に変換してキャッシュします(Translation Block)。キャッシュとチェイニングのおかげで、純粋なインタープリターよりはずっと速いですが、KVMと比べると、依然として10–100倍遅いです。
| 項目 | ネイティブ | QEMU+KVM | QEMU(TCG) |
|---|---|---|---|
| CPU演算 | 100% | 約98% | 約25% |
| ディスクI/O(virtio) | 100% | 約90% | 約30% |
| ネットワーク(vhost-net) | 100% | 約95% | 約25% |
このラボ環境には/dev/kvmがありません。コンテナにそのデバイスを入れていないからです。そのため、QEMUは-accel tcgで動きます。起動が遅いのは環境のせいで、設定ミスではありません。
virtio: 準仮想化の復活
エミュレートされたe1000 NICは、ゲストがレジスターに触れるたびにVM Exitを起こします。virtioは、ゲストとホストが共有メモリのリングバッファで通信するようにして、Exitを劇的に減らします。ゲストにvirtioドライバーが必要ですが、最近のLinuxには標準で入っています。
| デバイス | 比較した性能 |
|---|---|
| virtio-net | e1000の10倍以上 |
| virtio-blk | IDEエミュレーションの2–5倍 |
| virtio-scsi | 多数のディスクを接続するのに有利 |
| vhost-net | リングバッファの処理をカーネルで行う → さらに向上 |
性能の問題で報告が来たら、最初に尋ねることは「virtioを使っているか」です。テンプレートを間違えて選び、IDEで動いているVMが意外と多いのです。
現場での姿
ネステッド仮想化の落とし穴。クラウドVMの中でもう一度KVMを使うには、ネステッド仮想化が有効になっている必要があります。有効になっていないと、静かにTCGにフォールバックして、「なぜこんなに遅いのか」になります。/dev/kvmの有無と、qemu-system-x86_64 -accel helpの出力で確認します。
CPUモデルをhost-passthroughにしていないために性能が出ないケースです。既定のCPUモデルは、互換性のために、最新の命令拡張を隠します。AVXを使うワークロードなら、大きな差が出ます。その代わり、ライブマイグレーションの互換性が壊れるので、トレードオフです。
次の確認で見ること
続くクイズでは、KVMアクセラレーションとTCGフォールバック、virtioデバイス、CPUモデルの性能・移植性の条件を先に区別します。その判断を確認したあと、次のモジュールから、qcow2のバッキングファイルとスナップショット、QEMUのシリアルコンソール・モニターソケットをラボで扱います。