Virtual Machines: Versatile Platforms For Systems And Processes (The Morgan Kaufmann Series in Computer Architecture and Design)Virtual Machines: Versatile Platforms For Systems And Processes

VM本第三章やっと読み終わりました。ProcessVMについてです。

いろいろなVMがあるけど、ここではProcess or Program VMについて語られます。「仮想ABI」です。たとえばIA-32/WindowsのアプリケーションをItaniumのWindows上で動作させるものです。

3.1 Virtual Machine Implementation

Process VMのよくある構成について、主なブロック。

loader
guestのコードをロードする。
initialization
ロードされたあとに、初期化をするモジュール。code cacheとか、trapの登録とか、いろいろな前準備をする
emulation engine
インタープリットまたは・およびバイナリトランスレーションをする。プレデコードとかトランスレート結果はcode cacheに保持される。
code cache manager
コードキャッシュはたいてい限られているので、その管理をする。
profile database
ダイナミックに収集されたコードのプロファイル情報。最適化に使われる。
OS Call Emulator
OSとのインタラクションをエミュレートする。
Exception Emulator
Exception(trapや割り込み)をエミュレートする。
Side tables
トランスレートの信仰とともに生成されるデータ。用途はいろいろ。

3.2 Compatibility

ネイティブなプラットフォームとVM環境の互換性について。

3.2.1 Levels of Compatibility

"intrinsic compatibility"と"extrinsic compatibility"がある。

intrinsic compatibilityは"complete transparency"ともいわれる。多くの場合は完全な透過性はオーバースペック。

extrinsic compatibilityは、何らかの条件がつく。たとえば、特定のアプリケーションは動作するよ、とcertifyするなど。

3.2.2 A Comptibility Framework

intrinsicだろうがextrinsicだろうが、互換性があることを証明するのは大変むつかしい。その話をするためのフレームワークとして、まずはVMを構成要素に分解する。そして、各構成要素について(1)native環境とのステートマップ(2)ステート間遷移のマップをおこなう。このステートは、user-managedとOS-managedに分けることができる。

State Mapping

user-managedなステートマップはそんなに難しくない。guestのレジスタがhostのメモリにマップされるなどの場合があるが。OS-managed stateは、考え方が同じだがもうちょっと複雑。

Operations

user codeからOSへ制御がうつるポイントがキー。このマップがつくれれば、そのポイントでの状態が同等かどうかに集中できる。

Sufficient Compatibility Conditions

制御ポイントが明確になると、次のことがいえる:

  1. user code → OSのポイントで、ゲストの状態がホストの状態とマップ上同じである。ここからいえる重要なこと: 命令の粒度ではなく、OSへコントロールが渡る単位で状態の同等性をメンテナンスすればよい。
  2. OS → user codeのポイントでも、状態が同等。このとき、グラフィックとかネットワークとかみたいに、状態が同等なだけじゃなくてオペレーションの順番が同等である必要があるものもある。
Discussion

ここまでをふまえた議論。

3.2.3 Implementation Dependences

ISAの実装が、機能の差として顕れるケースがある。

たとえば、コードセグメントへの書き込みが、コードキャッシュにすぐ反映されない。

このような実装差が問題になるのは、たとえば、CPUの種別判定に使われる時。

逆に、仕様上は「コードキャッシュに反映されない」となっていても、実在の実装がすべてコードキャッシュに反映させているケースもある。そうすると、仕様に従って実装されたVMでは、現実のコード(自己変更含むもの)がうごかないかもしれない。「仕様に従っている」はいいわけにならない。

3.3 State Mapping

guest → targetの状態マップ。状態とはレジスタとメモリのことで、要はリソースなので、リソースマップと呼ぶこともある。

ここでのメモリは、論理メモリで実メモリスペースの話じゃない。

3.3.1 Register Mapping

レジスタマッピングはstraightforward(ってなんて訳すのがよいかな)。target側に充分なレジスタ数があれば、全部マップすればよいし、ダメならメモリにマップしたり、ダイナミックにマップを変更したりする。

3.3.2 Memory Address Space Mapping

メモリ空間のエミュレーションは、いろんなやり方がある。ソフトウェアの役割が大きいやりかたほど遅くなり、ハードウェアに任せる部分が多いほどパフォーマンスがよくなる。

Runtime Software-Supported Translation Tables

一番フレキシブル。ランタイムソフトウェアがメモリ管理。

でもバイナリトランレート環境ではかなり遅い。最後の手段。

Direct Translation Methods

単純なマッピング。同一アドレスか、単にオフセットを与えるかだけ。

Compatibility Issues

メモリマッピングとアドレス変換に何を使うか、はパフォーマンス・互換性の要求と深い関係がある。

メモリサイズや、ランタイムがアドレス空間を共用するか、など。

extrinsic compatibilityでよい場合も多い。

3.4 Memory Architecture Emulation

メモリアーキテクチャのエミュレーションで、考慮しなきゃいけないのは:

  • アドレス空間の構成: セグメントがあるのか、連続なのか。
  • アクセス権: R/W/Eがあるのか。RWのみか。
  • 保護・アロケーションの粒度:メモリブロックのサイズ
3.4.1 Memory Protection

メモリプロテクションは、もし変換テーブルが用意されていれば簡単。しかし直接またはオフセットアドレッシングだとそうはいかない。ホストのメモリプロテクション機能に頼ることもできるが、ページサイズがホスト・ゲストで異なるとか、実行中にメモリの権限が変更されることもある。

3.4.2 Self-Referencing and Self-Modifying Code

自己参照はオリジナルの参照をしているからOK。自己書き換えについて、基本的なやりかたはRead onlyにしておくこと。例外のハンドリングで自己書き換えを扱うことができる。

psude-self-modifing code

psude-self-modifingという概念がある。code pageのなかに、実際にデータが混じっているもの。デバイスドライバや組み込みのコードに見られる。psude-self-modifing codeについては、「頻繁にwrite exceptionがおきるとき、それがpsude-self-modifingかどうかチェックして、その場合はwrite protectionをはずす」という手がある。

Fine-Grain Write Protection

ページ単位ではなく、もっと細かいwrite protectionをソースのコードに用意する。ページ毎にbit mask

True Self-modifing Code

ほんとうにSelf-modifing codeがある場合(code regionにデータがあるのではなく、実際にcodeがかきかわる場合)は、idiom recognitionでself modifyじゃないコードに書き換える。

Protecting Runtime Memory

Omniware VMという例では、2の累乗サイズのページごとにアクセス権を設定。それにより、アドレスのシフト演算で権限チェックが簡単にできる。

ランタイム実行中と、エミュレーション中で権限を切り替えるやりかたがある(エミュレーション中はランタイムのアドレスにはアクセスできない、そしてCode Cacheは実行可能。ランタイム実行中は、Code Cacheは読み書き可能、など)。

レジスタがメモリ上にマップされているときの問題もある。

3.5 Instruction Emulation

(続く)