レベル: 中級 Christian Kaiser (christian.kaiser1@rwth-aachen.de), Research Intern, IBM Christian Rund (Christian.Rund@de.ibm.com), Research and Development Engineer, IBM
2008年 1月 08日 この 3 回連載の記事では、コンテナー仮想化 (オペレーティング・システム仮想化) として知られる、ハードウェア・リソースを中心としたソフトウェア仮想化について説明し、オープンソースのプロジェクトである OpenVZ を介してコンテナー仮想化の例を示します。ソフトウェアによる手法を使った Cell/B.E. プロセッサーの仮想化に必要なコンポーネントと手法のすべてを包括的に概説する連載の第 2 回目では、第 1 回で説明した専用仮想化とパーティショニングの実装について詳しく説明します。
今回は、最初の記事 (第 1 回の図 3) で説明した専用仮想化 (パーティショニング) の概念に対応した実装について説明します。共有仮想化デバイス (第 1 回の図 4) の概念の開発については、この記事では取り上げません。
システムの実装を説明するこの記事には、以下の内容が含まれます。
- spufs の仮想化: すべてのマウント・ポイントに同じ root-inode を使用させないようにします。
- sysfs の調整: 実行中にエントリーを適応させます。
- SPU スケジューラーの変更: 仮想化を実現するためのステップを追加します。
- OpenVZ ツールの変更: 新しい機能を利用します。
spufs の仮想化
デフォルトでは、マウントした spufs はすべて同じ root-inode を使用するように設定されています。例えば 2 つの chroot 環境 (A と B) があり、どちらの環境でもマウントされた spufs は /spu に設定されているとします。環境 A で SPE スレッドを作成して /spu に含まれる内容を表示すると、作成した SPE スレッドが表示されます。一方、環境 B で /spu の内容を表示すると環境 A と同じものが表示されます。これは、環境 A と同じ root-inode にアクセスしているためです。
この振る舞いを変更し、両方の環境でそれぞれに作成した SPE スレッドだけが表示されるようにするには、spufs がすべてのマウント・ポイントに同じ root-inode を使用するという事態を変えなければなりません。spufs はマウント時に、スーパー・ブロックを返すカーネル関数を内部で呼び出します。通常、この関数は get_sb_single() で、常に同じスーパー・ブロックを返します。そこで get_sb_nodev() 関数を使えば毎回異なるスーパー・ブロックを返すので、目的の振る舞いを達成することができます (図 1 に、仮想化を行う前と後の spufs を示します)。
図 1. 仮想化する前と後の spufs
OpenVZ では、コンテナー内に少数のファイル・システムしかマウントできません。それぞれのファイル・システムは struct file_system_type インスタンスを実装し、このインスタンスによってファイル固有の特徴が定義されます。OpenVZ は (数ある構造体のうち) この構造体を、ファイル・システムがマウント可能かどうかを定義するメンバーで拡張します。したがって、.fs_flags メンバーは FS_VIRTUALIZED に設定する必要があります。
コンテナー内部で作成されるプロセスには、2 つの PID が設定されます (第 1 回で説明したように、各コンテナーにはカーネルによって提供される独自のリソース・セットがあります。プロセス・ツリーは 1 つのリソース・セットで、そのなかでコンテナーが見られるのは仮想化 PID を持つ独自のプロセス・セットだけです)。コンテナー内部での PID は仮想化 PID ですが、ホスト・システム上では同じプロセスがもう 1 つの PID (グローバル PID) で表されます。前述したように、spufs ディレクトリーには spethread-<PID>-<thread-ID> という形式の名前が付きます。<PID> は SPE スレッドを保持するプロセスのプロセス ID、<thread-ID>> は対応するスレッド ID です。コンテナー環境では仮想化 PID であるため、コンテナー内に含まれる spufs のリストには、グローバル PID ではなく仮想化 PID が示されなければなりません。このような振る舞いは、幸い OpenVZ にすでに実装されています。
Linux® カーネルの arch/powerpc/platforms/cell/spufs/inode.c ファイルを変更してください。
sysfs の調整
sysfs はすでに仮想化されているので、コンテナー内で使用することができます。この sysfs はホスト・システムで表示される sysfs のコピーではなく、そのごく一部です。ここで目標となるのは、実行中に SPU をコンテナーに割り当て、コンテナーから解放することです。つまり、実行中に sysfs のエントリーも適応させなければなりません。この作業を行う前に、まずは SPU がリストされるディレクトリーを作成します。
このディレクトリーは /sys/devices/system/spu です。デフォルトでは、コンテナー環境内の sysfs には /sys/devices/system/spu、/sys/devices/system、/sys/devices のいずれのディレクトリーもないため、この 3 つのエントリーをコンテナーの初期化 (起動) 中に作成しなければなりません。/sys/devices/system/spu のサブディレクトリーは spu0 から spu<N> にすることができます。ここで、<N> はシステム内で利用可能な SPU の数から 1 を引いた値です。これらのディレクトリーは、SPU がコンテナーに割り当てられるときに作成され、コンテナーから解放されるときに削除される必要があります。
sysfs のリストに含まれる各ディレクトリーには、それぞれに対応する kobject インスタンスがカーネル空間内にあります。kobject とは、ディレクトリーの名前とその親 kobject (言い換えると、対応する親ディレクトリー) を定義する構造体のことです。例えば、sys/devices/system/spu/spu3 ディレクトリーとして後で表示される kobject については以下の 2 つの設定を調整します。
- 名前 spu3
- 親 (/sys/devices/system/spu を表す kobject へのポインター)
subsystem_register() を呼び出すことで kobject を sysfs に登録すると、kobject はユーザー空間内に表示されるようになります。反対に、sysfs からディレクトリーを削除するには、削除対象の kobject を指定して subsystem_unregister() 関数を呼び出します。図 2 に一例を示します。
図 2. sysfs および kobject
名前が spu3 となっている kobject には、名前が spu の親 kobject へのポインターがあります。
主な変更はカーネル・ファイル、kernel/ve/vecalls.c で行います。OpenVZ に固有のこのファイルには、初期化およびコンテナーのパラメーター設定を実行している間に呼び出されるほとんどの関数が実装されます。
SPU スケジューラーの変更
システム内にあるそれぞれの物理 SPU は、カーネル空間内では spu 構造体のインスタンスによって表されます。この構造体には、以下をはじめとする情報が含まれます。
- SPU の ID (番号)
- 所属先の Cell/B.E. ノード
- SPU の LS へのポインター
新しいメンバー変数は SPU のオーナーをコンテナー ID という形で保存します。SPU スケジューラーが実装する spu_alloc() 関数は、SPU スレッドを実行できる空き SPU を見つけるために、システム内で使用可能な (すぐに実行される SPU スレッドがない) SPU のリストを検索します。
通常の振る舞いでは、リストに含まれる最初の SPU で SPE スレッドが実行されます。仮想化の振る舞いを実現するには、この spu_alloc() 関数で、空き SPU と実行対象の SPE スレッドが同じコンテナー ID を持っていることをチェックしなければなりません。図 3 に、変更を行う前と後の spu_alloc() の動作を示します。
図 3. 変更前と変更後の spu_alloc()
この追加チェックの結果が真でない場合、関数は空き SPU のリストにある次の SPU をチェックします。SPE スレッドを起動するコンテナーに使用可能な空き SPU がない場合には、SPU スケジューラーはリストが空であるかのように振る舞い、SPU が空きになるまで待機します。
この spu_alloc() 関数を実装する場所は、Linux カーネルのソース・ファイル、arch/powerpc/platforms/cell/spu_base.c です。
OpenVZ ツールの変更
必要な関数のほとんどはすでに存在していますが、新しい機能を使用するために OpenVZ ツールを変更する必要があります。vzctl は実行中に SPU 割り当てを管理するツールです。OpenVZ でコンテナーのパラメーターを設定するには、主にこのツールを使用します。コンテナーに割り当てる SPU の数を設定するための新規パラメーターは、--spus <nr_spus> です。
<nr_spus> の値は、コンテナーに割り当てられる SPU の数を表します。この値は絶対数を表しているので、6 の設定値で 8 基の SPU がコンテナーに割り当てられている場合、6 基の SPU が追加されるのではなく、2 基の SPU がコンテナーから解放されます (8 - 6 = 2)。
以下のコマンド・ラインの出力例では、ID が 101 に設定されたコンテナーが 8 基の SPU を取得しています。
[root@c02b12-0 ~]# vzctl set 101 --spus 8
Setting SPUs: 8
Configure meminfo: 1024000
WARNING: Settings were not saved and will be reset to original
values on next start (use --save flag)
[root@c02b12-0 ~]#
|
この振る舞いを完成するには、vzctl ツールがユーザー空間の境界線を越えてカーネル空間で管理を行う必要があります。また、まだ他のコンテナーで使用されていない SPU を見つけることも必要です。vzctl ツールは使用可能な SPU のリストを検索し、spu 構造体内に新しく実装されたコンテナー ID をチェックします (SPU スケジューラーの変更についてのセクションで説明)。その値が 0 であれば、SPU を要求側コンテナーに割り当てることができます。0 という値が使用される理由は、コンテナー ID の値は 0 より大きいことが条件であるため、値 が 0 の場合には SPU が他のコンテナーに割り当てられていないことを意味するからです。関数が要求を完了できるだけの空き SPU を見つけられない場合、この手順は終了し、コンテナーに SPU は割り当てられません。コンテナーにすでに割り当てられている SPU の数が要求された SPU 数を上回る場合には、その差の分だけ SPU が解放されます。
ユーザー空間とカーネル空間との間の境界線を超えるには、さまざまな実装モデルを使用することができます (実装モデルの詳細については、「参考文献」で紹介している Arnd Bergmann の「How to not invent kernel interfaces」を参照してください)。そのうち最も単純な方法は、システム・コールのパラメーターに <containerID> および <nr_spus> パラメーターをマッピングする新しいシステム・コールを実装することです。
カーネル・モジュールとしてビルド可能なカーネルの一部には、コンテナーの SPU パラメーターの設定を操作する関数を実装する必要がありますが、これには大きな問題が伴います。カーネル・モジュールがロードされない場合にはカーネル空間内のシステム・コール・ハンドラー関数は何も実行せず、カーネル・モジュールがロードされた場合にはモジュール内に実装された関数が呼び出されるようにしなければなりません。しかし、システム・コール・テーブル (システム・コール・ハンドラー関数への関数ポインターが常駐するテーブル) は静的カーネル・ビルドの一部であるため、そう簡単には行きません。
このモジュールは静的関数の一部ではないことから、静的組み込みシステム・コール・ハンドラー関数がモジュールの一部である関数を呼び出すことは不可能です。この問題に対するソリューションは、モジュール内の関数へのポインターを静的組み込みシステム・コール・ハンドラー関数の変数にコピーする関数ラッパーを実装し、静的に組み込まれたシステム・コール・ハンドラーがモジュール内の関数を呼び出せるようにすることです。この関数ラッパーを、モジュールの初期化とクリーンアップの際に呼び出すようにします。図 4 では、関数ラッパー・メソッドを黒の矢印で示しています。
図 4. システム・コールおよびモジュールの関数
上記の図から、モジュール内部に実装された関数の関数ポインターが静的組み込みカーネル空間にコピーされる仕組みがわかるはずです。点線の矢印は、ユーザー空間のアプリケーションが静的組み込みシステム・コール・ハンドラー関数を渡すことによって、モジュール内の関数を呼び出していることを示しています。
変更が必要なカーネルのソース・コード・ファイルは以下のとおりです。
- include/asm-powerpc/systbl.h
- include/asm-powerpc/unistd.h
- include/linux/syscalls.h
- kernel/sys.c
- kernel/sys_ni.c
- kernel/ve/vecalls.c
また、OpenVZ の vzctl ソースに含まれる以下のファイルも更新してください。
- include/res.h
- include/vzctl_param.h
- include/vzsyscalls.h
- src/lib/config.c
- src/lib/res.c
- src/vzctl.c
さらに以下の 2 つのファイルを導入し、OpenVZ ビルド・システムに組み込みます。
- include/spu.h
- src/lib/spu.c
第 3 回の予告
第 3 回では、システムを使用してテストする方法を説明し、コンテナー仮想化のパフォーマンスを疑似仮想化や完全仮想化などの他のソフトウェア仮想化方法と比較して分析します。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | |  | Christian Kaiser は、ドイツ・アーヘンにある RWTH University でコンピューター工学を学び、2007年に研修生としてボーブリンゲンの IBM Germany Research Lab に勤務しました。IBM での研修期間中に研究したのは、Cell Broadband Engine プロセッサーを対象とした仮想化の手法です。この研修期間の後、彼は RWTH Aachen の Chair for Operating Systems で論文に取り組むため大学に戻りました。論文のテーマは、「Analysis of asynchronous collective communication in memory-coupled high-speed networks」です。 |
 | |  | Christian Rund は、ドイツ・ボーブリンゲンの IBM Development Laboratory に勤務しています。彼は University of Stuttgart とスウェーデンの Uppsala Universitet でコンピューター・サイエンスを学び、1997年に卒業しました。学生時代にドイツのヘレンベルグとシュトュットガルトの IBM で研修生として勤務した経験を経て、1998年にシュトュットガルト所在の Landeszentralbank (Deutsche Bundesbank) システム開発部門の一員となりました。zSeries FCP チャネル開発チームの研究開発技術者として IBM に入社したのは 2001年です。その後、2006年中頃からは Cell/B.E. ベースのサーバー向けホスト・ファームウェアの研究開発技術者として活躍しています。 |
記事の評価
|