virtio: Linux の I/O 仮想化フレームワーク

KVM と lguest による準仮想化 I/O

Linux カーネルは多様な仮想化方式をサポートしています。このサポートの幅は、仮想化技術の進化、そして新しい仮想化方式の発見 (例えば lguest など) に伴って広がっていくはずです。では、こうした Linux 上で機能する仮想化方式では、どのようにしてベースとなる Linux カーネルを I/O 仮想化に利用しているのでしょうか?その答えは virtio です。すべての仮想化方式で使用されている virtio はハイパーバイザー、そして共通する一連の I/O 仮想化ドライバーを効率的に抽象化してくれます。この記事では virtio を紹介するとともに、近いうちに Linux がハイパーバイザーの選択肢として第一の存在になると思われる理由について説明します。

専門知識をお持ちの方へ:  どのハイパーバイザーを使用するかを決定する上で、ハイパーバイザーが特定の I/O 仮想化方式をサポートしているかどうかが影響しますか?記事の終わりにあるコメント欄にコメントをお寄せください。

M. Tim Jones, Independent author

M. Tim JonesM. Tim Jones は組み込みソフトウェアのエンジニアであり、『Artificial Intelligence: A Systems Approach』、『GNU/Linux Application Programming』現在、第 2 版です) や『AI Application Programming』(こちらも現在、第 2 版です)、それに『BSD Sockets Programming from a Multilanguage Perspective』などの著者でもあります。技術的な経歴は静止軌道衛星用のカーネル開発から、組み込みシステム・アーキテクチャーやネットワーク・プロトコル開発まで、広範にわたっています。また、コロラド州ロングモン所在のEmulex Corp. の顧問エンジニアでもあります。


developerWorks 貢献著者レベル

2010年 1月 29日

Tim とつながるには

Tim は developerWorks で人気の高いお馴染みの著者の 1 人です。Tim が書いたすべての developerWorks の記事を閲覧してみてください。また、My developerWorks では、Tim のプロフィールを調べることや、彼やその他の著者、そして他の読者とつながることができます。

一言で言うと、virtio は準仮想化ハイパーバイザー内のデバイスを抽象化する層です。virtio は、Rusty Russell が彼独自の仮想化ソリューション lguest を支援するために開発しました。この記事ではまず準仮想化と、エミュレートされるデバイスについて概説した後、virtio の詳細を探ります。この記事で焦点とするのは、2.6.30 カーネル・リリースの virtio フレームワークです。

Linux はハイパーバイザーの活躍の場です。ハイパーバイザーとしての Linux に焦点を当てた記事で私が説明したように、Linux はそれぞれに異なる特質と利点を備えた多種多様なハイパーバイザー・ソリューションを提供しています。例えば、KVM (Kernel-based Virtual Machine)、lguest、User Mode Linux などです。これら各種のハイパーバイザー・ソリューションを Linux で使用すると、それぞれのソリューション独自の必要に応じた負担がオペレーティング・システムに課せられることになります。負担の 1 つは、デバイスの仮想化です。ドライバー (ネットワーク・ドライバー、ブロック・ドライバー、その他のドライバー) ごとに異なるデバイス・エミュレーション・メカニズムを使用する代わりの手段となる virtio は、インターフェースを標準化してプラットフォーム間でのコードの再利用を促すために、これらの各種デバイス・エミュレーションに共通のフロントエンドを提供します。

完全仮想化と準仮想化の違い

My developerWorks の GReen グループに参加してください

エネルギー、効率性、環境に関するトピックについて議論し、リソースを共有するために、My developerWorks の GReen IT Report スペースGreen computing group に参加してください。

まずは、完全仮想化と準仮想化という 2 つの異なる仮想化方式について簡単に説明しておきます。完全仮想化では、ゲスト・オペレーティング・システムはベア・メタル上にある (つまりハードウェア上で実行される) ハイパーバイザー上で直接動作します。この場合、ゲスト・オペレーティング・システムは、仮想化が行われていることを認識することはありません。また、この仮想化構成で機能するためにゲスト・オペレーティング・システムに手を加える必要もありません。それとは逆に準仮想化では、ゲスト・オペレーティング・システムはハイパーバイザー上で動作していることを認識します。さらに、ゲスト・オペレーティング・システムには、ハイパーバイザーとのインターフェースを効率化するためのコードが組み込まれます (図 1 を参照)。

完全仮想化方式の場合、ハイパーバイザーはデバイス・ハードウェアをエミュレートしなければなりません。これは、最下位レベルでの (例えば、ネットワーク・ドライバーの) エミュレーションになります。この抽象化レベルでエミュレーションをすると、明確ではあるものの、最も非効率的で極めて複雑な作業になります。一方、準仮想化方式ではゲスト・オペレーティング・システムとハイパーバイザーが連携して動作することができるので、エミュレーションが効率化されます。ただし準仮想化方式の場合、ゲスト・オペレーティング・システムは、仮想化が行われていることを認識することになり、仮想化構成で機能するためにはゲスト・オペレーティング・システムに手を加える必要があるという欠点があります。

図 1. 完全仮想化環境と準仮想化環境でのデバイス・エミュレーション
完全仮想化環境と準仮想化環境でのデバイス・エミュレーション

ハードウェアは仮想化とともに変化の一途を辿っています。新しいプロセッサーには、ゲスト・オペレーティング・システムとハイパーバイザーとのインターフェースを効率化する高度な命令が組み込まれるようになってきています。さらに、ハードウェアは入出力 (I/O) 仮想化に関しても変化し続けています (PCI (Peripheral Controller Interconnect) パススルー技術や、シングルルートおよびマルチルート I/O 仮想化について学ぶには、「参考文献」を参照してください)。

virtio の代わりとなる手段

この領域における選択肢は virtio だけではありません。Xen では準仮想化デバイス・ドライバーを提供していますし、VMware にもゲスト・ツールという代替手段があります。

しかし従来の完全仮想化環境では、ハイパーバイザーがまずリクエストをトラップし、それから実在のハードウェアの振る舞いをエミュレートしなければなりません。これによって最大限の柔軟性がもたらされるものの (つまり、オペレーティング・システムをそのまま実行できるということ)、同時に非効率性がもたらされることも確かです (図 1 の左側を参照)。図 1 の右側は、準仮想化の場合を示しています。準仮想化環境では、ゲスト・オペレーティング・システムはハイパーバイザー上で動作していることを認識します。そのため、ゲスト・オペレーティング・システムにはフロントエンドとして機能するドライバーが組み込まれ、ハイパーバイザーはその特定のデバイス・エミュレーションに対応するバックエンド・ドライバーを実装します。virtio の活躍の場となるのは、このフロントエンド・ドライバーとバックエンド・ドライバーです。エミュレートされたデバイスへアクセスするコードを開発する際に virtio を標準インターフェースとすることで、コードの再利用が促され、開発の効率が向上する結果となるはずです。


Linux ゲストの抽象化

前のセクションから理解できるように、virtio は準仮想化ハイパーバイザーでエミュレートされる共通デバイスをまとめて 1 つに抽象化します。この設計では、ハイパーバイザーは、エミュレートされた共通デバイス一式をエクスポートし、共通アプリケーション・プログラミング・インターフェース (API) を介してこれらのデバイスを使えるようにします。この点が重要である理由は、図 2 を見るとわかります。準仮想化ハイパーバイザーでは、図に示されているようにゲストが共通インターフェース一式を実装し、個々のデバイス・エミュレーションは一連のバックエンド・ドライバーの背後に隠されます。バックエンド・ドライバーがフロントエンド・ドライバーとのインターフェースを取る上で必要な振る舞いを実装している限り、ハイパーバイザー間でバックエンド・ドライバーが共通している必要はありません。

図 2. virtio によるドライバーの抽象化
virtio によるドライバーの抽象化

実際には (必須ではありませんが)、デバイスのエミュレーションはユーザー空間で QEMU を使用して行われます。そのため、バックエンド・ドライバーは QEMU を介した I/O 処理を容易にするために、ハイパーバイザーのユーザー空間と通信することに注意してください。QEMU はシステム・エミュレーターであり、ゲスト・オペレーティング・システムの仮想化プラットフォームとなるだけでなく、システム全体 (PCI ホスト・コントローラー、ディスク、ネットワーク、ビデオ・ハードウェア、USB コントローラー、およびその他のハードウェア要素) をエミュレートします。

virtio API は単純なバッファー抽象化を利用して、ゲストのコマンドおよびデータに対する要求をカプセル化します。次は、この virtio API の内部構造とそのコンポーネントについて説明します。


virtio のアーキテクチャー

フロントエンド・ドライバー (ゲスト・オペレーティング・システムに実装) とバックエンド・ドライバー (ハイパーバイザーに実装) に加え、virtio はゲストとハイパーバイザーの間の通信をサポートする 2 つの層を定義します。最上位レベルにあるのは、概念的にフロントエンド・ドライザーをバックエンド・ドライバーに接続する仮想キュー・インターフェースです (virtio と呼ばれます)。ドライバーはその必要に応じて、キューを使用しないことも、1 つまたは複数のキューを使用することもできます。例えば virtio ネットワーク・ドライバーは 2 つの仮想キュー (受信用と送信用) を使用する一方、virtio ブロック・ドライバーが使用する仮想キューは 1 つだけです。仮想キューは仮想であることから、実際にはゲストとハイパーバイザーとの間にまたがるリング・キューとして実装されます。ただし、ゲストとハイパーバイザーの両方で同じように実装されている限り、仮想キューはどのようにでも実装することができます。

図 3. virtio フレームワークのアーキテクチャー概要
アーキテクチャー概要

図 3 には、ブロック・デバイス (ディスクなど)、ネットワーク・デバイス、PCI エミューション、バルーン・ドライバー (ゲストのメモリー使用量を動的に管理するドライバー)、そしてコンソール・ドライバーごとの合計 5 つのフロントエンド・ドライバーが示されています。ハイパーバイザー内には、フロントエンド・ドライバーそれぞれに対応するバックエンド・ドライバーがあります。

概念階層

ゲストの観点から見たオブジェクト階層は、図 4 のように定義されます。最上位にある virtio_driver が表すのは、ゲスト内のフロントエンド・ドライバーです。このドライバーに対応するデバイスをカプセル化する virtio_device (ゲスト内のデバイスの表現) は、virtio_config_ops 構造 (virtio デバイスを構成するための操作を定義) を参照します。この virtio_device を参照するのは、virtqueue (サービス対象の virtio_device の参照が含まれます) です。それぞれの virtqueue オブジェクトは、該当するハイパーバイザー・ドライバーを扱うための基本キュー操作を定義する virtqueue_ops オブジェクトを参照します。キュー操作は virtio API のコアですが、ディスカバリーについて簡単に説明してから、virtqueue_ops 操作について詳しく説明したいと思います。

図 4. virtio フロントエンドのオブジェクト階層
virtio フロントエンドのオブジェクト階層

プロセスは virtio_driver を作成し、register_virtio_driver によって登録するところから始まります。virtio_driver 構造体が定義するのは、上位レベルのデバイス・ドライバー、そのドライバーがサポートするデバイス ID のリスト、機能テーブル (デバイスのタイプに依存)、そしてコールバック関数のリストです。ハイパーバイザーがデバイス・リストに含まれるデバイス ID と一致する新しいデバイスの存在を特定すると、(virtio_driver オブジェクトの中で指定されている) probe 関数が呼び出され、この関数に virtio_device オブジェクトが渡されます。このオブジェクトは (ドライバーに応じた方法で) デバイスの管理データと一緒にキャッシュされます。ドライバーのタイプによっては、デバイスに固有のオプションを取得または設定するために virtio_config_ops 関数が呼び出される場合もあります (例えば、virtio_blk デバイスの場合には、ディスクの読み取り/書き込みステータスが取得されるか、またはブロック・デバイスのブロック・サイズが設定されます)。

virtio_device には、virtqueue への参照が一切含まれていないことに注意してください (その一方で、virtqueuevirtio_device を参照します)。この virtio_device に関連付けられた virtqueue を特定するには、find_vq 関数と virtio_config_ops オブジェクトを使用します。このオブジェクトが、virtio_device インスタンスと関連付けられた仮想キューを返します。find_vq 関数には、virtqueue (virtqueue図 4 の構造体を参照) のコールバック関数を指定することもできます。指定されたコールバック関数は、ゲストにハイパーバイザーからのレスポンス・バッファーを通知するために使用されます。

virtqueue は単純な構造体で、この構造体で指定されるのはオプションのコールバック関数 (ハイパーバイザーがバッファーを使用すると呼び出されます)、virtio_device への参照、virtqueue 操作への参照、そして使用する基本実装を参照する特殊な priv 参照です。コールバックはオプションですが、動的にコールバックを有効または無効にすることができます。

ここまで virtio の階層について説明してきましたが、この階層のコアとなるのは virtqueue_ops です。このオブジェクトによって、ゲストとハイパーバイザーとの間でどのようにコマンドとデータのやりとりをするかが定義されます。そこで、まずは virtqueue に追加、またはそこから削除される virtqueue_ops のオブジェクトについて説明します。

>virtio のバッファー

ゲスト (フロントエンド) ドライバーは、バッファーを介してハイパーバイザー (バックエンド) ドライバーと通信します。I/O の場合、ゲストはそのリクエストを表す 1 つ以上のバッファーを提供します。例えば 3 つのバッファーを提供して、最初のバッファーで読み取りリクエストを表し、残りの 2 つのバッファーでレスポンス・データを表すといったことが可能です。内部では、この構成は (リスト内の各エントリーがアドレスと長さを表す) スキャッター・ギャザー・リストとして表現されます。

コア API

ゲスト・ドライバーとハイパーバイザー・ドライバーとの関連付けは virtio_device によって行われますが、大抵の場合は virtio_device を参照する virtqueue を通じて行われます。virtqueue には、5 つの関数からなる独自の API があります。まず、add_buf はリクエストをハイパーバイザーに送信するために使用する関数です。送信されるリクエストは、前に説明したスキャッター・ギャザー・リストの形をとります。ゲストは add_buf に対し、リクエストをキューに入れるための virtqueue、スキャッター・ギャザー・リスト (アドレスと長さの配列)、(ベースとなるハイパーバイザーへの) 出力エントリーの役割を果たすバッファーの数、(ハイパーバイザーがデータを保管し、ゲストに返す) 入力エントリーの数を指定します。add_buf 関数によってハイパーバイザーにリクエストを送信した後、ゲストは kick 関数を使用して、新規リクエストをハイパーバイザーに通知することができます。パフォーマンスを最大にするためには、ゲストが kick 関数で通知を行う前に、できるだけ多くのバッファーを virtqueue に積んでおくことが重要です。

ハイパーバイザーからのレスポンスは、get_buf 関数を通じて取得されます。ゲストは単純にこの関数を呼び出してレスポンスをポーリングすることも、virtqueue に指定したコールバック関数によって通知されるまで待機することもできます。該当するバッファーが使用可能であるとゲストが知った時点で、get_buf の呼び出しによって完全なバッファーが返されます。

virtqueue API の残りの 2 つの関数は、enable_cbdisable_cb です。この 2 つの関数を使用して、コールバック・プロセスを有効または無効にすることができます (その手段として使用されるのは、find_vq 関数によって virtqueue で初期化されたコールバック関数です)。コールバック関数とハイパーバイザーはそれぞれ別のアドレス空間にあるため、コールバック関数の呼び出しは、ハイパーバイザーを間接的に呼び出すことによって行われることに注意してください (kvm_hypercall 呼び出しなど)。

バッファーのフォーマット、順序、内容は、フロントエンド・ドライバーとバックエンド・ドライバーに対してのみ意味を持ちます。内部トランスポート (現行の実装でのリング) はバッファーだけを移動させるので、その内部表現についての情報は一切持ちません。


virtio ドライバーの例

Linux カーネルの ./drivers サブディレクトリーの中には、さまざまなフロントエンド・ドライバーのソースがあります。例えば virtio ネットワーク・ドライバーは ./drivers/net/virtio_net.c に、virtio ブロック・ドライバーは ./drivers/block/virtio_blk.c にあります。./drivers/virtio サブディレクトリーには、virtio インターフェース (virtio デバイス、ドラバー、virtqueue、およびリング) の実装が用意されています。これまで virtio は、HPC (High-Performance Computing) の研究でも、共有メモリーの受け渡しによる内部仮想マシン (VM) 通信を開発するために使用されました。具体的に言うと、この内部 VM 通信は、virtio PCI ドライバーを使用した仮想化 PCI インターフェースを介して実装されています。この取り組みに関する詳細は、「参考文献」セクションを参照してください。

この準仮想化インフラストラクチャーは、現在 Linux カーネルで実際に使用することができます。そのために必要なのは、ハイパーバイザーとして機能するカーネル、ゲスト・カーネル、そしてデバイスをエミュレートするための QEMU だけです。この仮想化ソリューションには、KVM (ホスト・カーネルにあるモジュール) を使用することも、Rusty Russell の lguest (Linux ゲスト・カーネルの変更バージョン) を併せて使用することもできます。どちらを使用した仮想化ソリューションにしても、(システム・エミュレーション用の QEMU と仮想化管理用の libvirt と併せて) virtio をサポートします。

Rusty の研究は、準仮想化ドライバーのコード・ベースを単純化し、仮想デバイスのエミュレーションを高速化するという成果を上げました。しかしそれよりも重要なのは、virtio は明らかに、現在の商用ソリューションよりも優れたパフォーマンス (ネットワーク I/O については 2 倍から 3 倍) を実現するという点です。このパフォーマンスの向上には犠牲を伴いますが、Linux をハイパーバイザーおよびゲストとして使用しているならば、その犠牲を払う価値は十分にあります。


さらに詳しく調べてください

virtio のフロントエンド・ドライバーやバックエンド・ドライバーを開発した経験がないとしても、興味深いアーキテクチャーを実装する virtio の詳細は理解しておく価値があります。virtio は Xen での以前の取り組みをベースに、準仮想化 I/O 環境を効率化する新たな可能性を広げます。Linux は今後も、本番用ハイパーバイザーとして、そして新しい仮想化技術の研究プラットフォームとしての実力を示し続けるはずです。virtio は、ハイパーバイザーとしての Linux に備わった強みと開放性を示す 1 つの例でしかありません。

参考文献

学ぶために

  • virtio の技術的詳細を深く掘り下げるのに最適な情報源の 1 つは、Rusty Russell の「Virtio: towards a de factor standard for virtual I/O devices」です。この文書では、virtio とその内部構造を完全網羅しています。
  • _この記事では、完全仮想化と準仮想化という 2 つの仮想化方式を取り上げました。Linux でのさまざまな仮想化方式について詳しく学ぶには、Tim の記事「仮想 Linux」(developerWorks、2006年12月) を読んでください。
  • virtio の背後を支える鍵は、準仮想化を利用した全体的な I/O パフォーマンスの改善です。Linux が果たすスーパーバイザーとしての役割、そしてデバイス・エミュレーションにおける役割については、Tim の 2 つの記事、「Linux ハイパーバイザーの徹底調査」(developerWorks、2009年5月) と「Linux の仮想化と PCI パススルー」(developerWorks、2009年10月) のそれぞれで詳しく説明しています。
  • この記事ではデバイス・エミュレーションについて触れましたが、QEMU (システム・エミュレーター) はこの機能を提供する上で最も重要なアプリケーションの 1 つです。QEMU についての詳細は、Tim の記事「QEMU によるシステムのエミュレーション」(developerWorks、2007年9月) で説明しています。
  • Xen にも準仮想化ドライバーの概念が統合されています。Paravirtual Windows Drivers では、特に準仮想化とハードウェア支援による仮想化 (HVM) の 2 つの仮想化を取り上げています。
  • virtio がもたらす利点のなかで特に重要なのは、準仮想化環境でのパフォーマンスです。この btm.geek によるブログ投稿では、KVM を使用した virtio のパフォーマンスにおける優位性を明らかにしています
  • この記事では libvirt (オープン仮想化 API) と virtio フレームワークとの接点に触れました。libvirtvirtio デバイスを指定する方法は、libvirt ウィキで説明しています。
  • この記事では virtio フレームワークを利用した 2 つのハイパーバイザー・ソリューションについて説明しました。一方の lguest は、同じく Rusty Russell によって開発された x86 ハイパーバイザーです。そしてもう一方の KVM は、Linux カーネルに初めて統合された Linux ベースのハイパーバイザーです。
  • virtio の興味深い適用法の 1 つは、SpringerLink のこの文書で説明しているように、ハイパーバイザーを介した VM 間の通信を可能にする、共有メモリー・メッセージの受け渡しの開発です。
  • developerWorks Linux ゾーンに豊富に揃った Linux 開発者向けの資料を調べてください。記事とチュートリアルの人気ランキングも要チェックです。
  • developerWorks に掲載されているすべての「Linux のヒント」シリーズの記事と Linux チュートリアルを参照してください。
  • developerWorks の Technical events and webcasts で最新情報を入手してください。
  • Twitter で developerWorks をフォローしてください。

製品や技術を入手するために

  • developerWorks から直接ダウンロードできる IBM ソフトウェアの試用版を使用して、Linux で次の開発プロジェクトを構築してください。

議論するために

  • My developerWorks コミュニティーに加わってください。ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者が主導するブログ、フォーラム、グループ、ウィキを調べることができます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux
ArticleID=470181
ArticleTitle=virtio: Linux の I/O 仮想化フレームワーク
publish-date=01292010