仮想化と入出力

仮想化を行う場合のもう1つの大問題は入出力(I/O)である。単純に普通のOSをゲストOSとして動かすと、それぞれのゲストOSがI/Oを占有していると思って、自分のデバイスドライバでI/Oハードウェアを制御してしまう。これでは、I/Oの動作は滅茶苦茶になってしまう。

基本的な解決方法は、VMMにI/Oデバイスのソフトウェアモデルを持ち、ゲストOSはI/Oデバイスの制御レジスタを読み書きしてI/O動作を行っているつもりであるが、ユーザ状態での制御レジスタの読み書きは特権違反となり、VMMのソフトウェアモデルがゲストOSの指令を変換して、整合の取れた形で実I/Oデバイスを動かすという方法である。

原理的にはこれで良いのであるが、各種のI/Oデバイスに対してそのデバイス専用のデバイスドライバを騙すほどそっくりで、かつ、矛盾なく複数のゲストOSからのI/O処理をサポートするソフトウェアシミュレータを作るのは容易ではない。また、RS-232Cのような低速シリアルインタフェースならソフトウェアシミュレーションでも性能的に問題ないが、高速の転送を必要とするディスクやLANアダプタでは、性能ががた落ちになってしまうという問題がある。

これを避ける1つの方法は、I/Oデバイスを1つのゲストOSに専用的に割り当てるというやり方である。このやり方では、それぞれのゲストOSは使用するI/Oデバイスを占有しているので、通常のデバイスドライバがそのまま動作する。ただし、DMAを持っているようなデバイスの場合は、ゲストOS配下のデバイスドライバは、データを転送するメモリアドレスをゲストアドレスでI/Oデバイスに指定してしまうので、前記のメモリ管理と同様の問題が発生する。これを避けるには、図10.11に示すように、I/Oデバイスとのデータ転送専用のTLBを設け、ゲストOSとI/Oデバイスはゲストアドレスに転送を行っていると思っているのであるが、実はVMMが制御するI/Oデバイス専用のTLBで再度、アドレス変換を行って物理アドレスに変換するという方法が取られる。

図10.11 I/O専用のTLBを持つ構造

また、TLBが書き込み不可とか実行不可のページの属性をチェックする機能を持つのと同様に、IOTLBもそれぞれのI/Oデバイスからのアクセスが正当であるかどうかのチェックを行う機能をもっている。

このIOTLBは物理的にはI/Oを接続するハブチップに実装されるのが一般的であり、1つのハブチップがサポートするすべてのI/OデバイスでこのIOTLBを共用する。しかし、I/Oデバイスごとにページテーブルが異なり、また、そのページテーブルがゲストOSの切り替わりにより切り替えられるということも可能であるので、この構造は十分なフレキシビリティをもっている。

なお、IOTLBが無い場合は、VMMが転送先の物理アドレスを教えるサービスを提供し、ドライバはそれを利用して物理アドレスをI/OデバイスのDMAコントローラに設定するように改造する、あるいは、ゲストOSのデバイスドライバがDMAアドレスを設定するのを検出して例外を発生してVMMが転送アドレスを書き換えれば正しく動作するが、デバイスドライバの修正やI/OデバイスのモデルでDMAアドレスレジスタの設定を検出して例外を発生するような機構が必要となる。また、バグや故意で、デバイスドライバがOSの領域や別のアプリケーションの領域のアドレスを指定してしまうとセキュリティ上の問題が発生してしまうが、IOTLBを使って仮想化し、VMMがアクセスの正当性を確認することにより、このような干渉を排除することができる。

I/Oデバイスを専用的に1つのゲストOSに割り付ける方法は既存のデバイスドライバが使えるというメリットがあり、また、性能的な低下もないというメリットがある良い方法であるが、ディスクやLANアダプタのようなデバイスの場合、仮想プロセサごとに物理的なディスクやLANを実装するのはコスト面では実用的とは言えない。このような場合には、VMM側にデバイスドライバなどのデバイスを操作するドライバプログラムを持ち、各ゲストOSからは、VMMにディスクR/Wやネットワークとのパケットの送受などのハイレベルの操作を依頼するという方法が考えられる。この方法は性能も比較的高く、I/Oデバイスの共用が可能になるが、ゲストOSは自分のデバイスドライバではなく、VMMのデバイスドライバに処理を依頼するように書き換える必要がある。このようにVMMが効率よく動けるように、ゲストOS側にもある程度の変更を行うという仮想化のやり方をParavirtualizationと呼ぶ。最近のVMMでは、各種のI/O仮想化法をサポートするのが普通であり、ディスクやLANに対しては、Paravirtualizationもサポートしているというのが一般的である。

このように仮想化は、各処理の負荷のピークが重なる確率が小さいことを利用して、プロセサなどのハードウェア資源を共有することにより、必要な総資源を減らすことができるが、ゲストOSやアプリケーションを切り替えるたびにメモリイメージをディスクとの間で出し入れしていては、論理的には正しく動くとしても、切り替えオーバヘッドが大きくて性能が出ない。したがって、効率よく切り替えを行うためには、VMMが実行するゲストOSとアプリケーションのワークセットの大部分が常にメモリに乗っている必要がある。

各ゲストOSに必要な最大のメモリ量やディスク容量の合計のリソースを持つ必要はないが、仮想化を効率よく動かすには、"平均的に必要な量"×"ゲストOS数"+α程度のメモリやディスクのワークスペースが必要となる。したがって、VMMを導入すれば、既存の1台のサーバハードウェアがそのままで多数の仮想サーバに化けるというほど美味しい話ではないが、個々の仮想サーバのプロセサ、メモリ、ディスクなどのピーク負荷時の必要量の合計ではなく、平均値の合計+ガードバンドの資源で済むという点でサーバハードウェアのコスト削減や消費電力の削減でコストメリットが大きく、仮想化を利用したサーバコンソリデーション(複数のサーバを1台のサーバに纏める)は最近の流行である。