5

一部のエミュレーターと仮想マシンは動的再コンパイルを使用していることに気づきました。彼らはどのようにそれをしますか?Cでは、型キャストを使用してRAM内の関数を呼び出す方法を知っていますが(試したことはありませんが)、オペコードを読み取ってそのコードを生成するにはどうすればよいですか?その人は事前に作成されたアセンブリチャンクを持っていて、それらを一緒にコピー/バッチ処理する必要がありますか?アセンブリはCで書かれていますか?もしそうなら、どのようにしてコードの長さを見つけますか?システム割り込みをどのように説明しますか?

-編集-

システム割り込みとデータの(再)コンパイル方法が私が最も興味を持っているものです。さらに調査したところ、ある人(ソースが利用できない)がjsを使用し、マシンコードを読み取り、jsソースを出力し、evalを使用して「コンパイル」したと聞きました。 jsソース。面白い。

4

7 に答える 7

1

明らかに些細なことではありませんが、メモリポインタからコードを逆アセンブルし、何らかの方法でコードを最適化してから、最適化されたコードを元の場所または元の場所にパッチを適用した新しい場所に書き戻すことは非常に可能です。 。

もちろん、エミュレーターとVMは再書き込みする必要はなく、ロード時にこれを行うことができます。

于 2010-04-09T22:23:35.227 に答える
1

動的に再コンパイルするには、ターゲットプラットフォームのマシンコードの知識が必要なようです

そのとおり。そのため、Java仮想マシンの一部(つまり、JIT)をアーキテクチャごとに書き直す必要があります。

仮想マシンを作成するときは、特定のホストアーキテクチャと特定のゲストアーキテクチャを念頭に置いています。ポータブルVMは、ゲストアーキテクチャのすべての命令をエミュレートするため、エミュレータと呼ばれる方が適切です(ゲストレジスタは、ホストレジスタではなくホスト変数として表されます)。

VMWareのように、ゲストアーキテクチャとホストアーキテクチャが同じである場合、仮想化を高速化するために実行できる(かなりきちんとした)最適化がたくさんあります。今日、このタイプの仮想マシンは、プロセッサ上で直接実行されます。もちろん、これはアーキテクチャに大きく依存します。移植を試みるよりも、ほとんどのVMWareを最初から書き直す方がよいでしょう。

于 2010-04-13T04:17:07.920 に答える
1

これは広く開かれた質問であり、どこに行きたいのかわかりません。ウィキペディアは、一般的なトピックを一般的な回答でカバーしています。エミュレートまたは仮想化されているネイティブコードは、ネイティブコードに置き換えられます。コードが実行されるほど、より多くが置き換えられます。

いくつかのことを行う必要があると思います。まず、エミュレーションについて話しているのか、vmwareやvirtualboxなどの仮想マシンについて話しているのかを判断します。プロセッサとハードウェアのエミュレーションはソフトウェアを使用してエミュレートされるため、次の命令がエミュレータによって読み取られ、オペコードがコードによって引き離され、それをどのように処理するかを決定します。私はいくつかの6502エミュレーションと静的バイナリ変換を行ってきました。これは動的再コンパイルですが、リアルタイムではなく前処理されています。したがって、エミュレーターはLDA#10を取得し、即時にロードします。エミュレーターはロードを確認します。即時命令は、次のバイトを読み取る必要があることを認識します。これは、エミュレーターがAレジスターのコードに変数を持ち、その変数の即値。命令を完了する前に、エミュレータはフラグを更新する必要があります。この場合、ゼロフラグはクリアです。Nフラグはクリアです。CとVは変更されていません。しかし、次の命令がロードX即時だった場合はどうなるでしょうか?大したことじゃない?ロードxはzフラグとnフラグも変更するので、次にロード命令を実行するときに、フラグが破棄されるため、フラグを計算する必要がないことがわかります。これはエミュレーションのデッドコードです。この種の考え方を続けることができます。たとえば、xレジスタをレジスタにコピーし、次にaレジスタをスタックにプッシュし、次にyレジスタをレジスタにコピーしてスタックにプッシュするコードを見ると、そのチャンクを置き換えることができます。スタック上のxレジスタとyレジスタをプッシュするだけです。または、16ビットの加算を実行して結果を隣接するメモリ位置に格納するために、キャリーがチェーンされたいくつかの加算が表示される場合があります。基本的に、エミュレートされているプロセッサでは実行できなかったが、エミュレーションでは簡単に実行できる操作を探します。動的再コンパイルの前に調べることをお勧めする静的バイナリ変換は、コードを実行する前のように、静的な方法でこの分析と変換を実行します。たとえば、エミュレートする代わりに、オペコードをCに変換し、できるだけ多くのデッドコードを削除します(優れた機能は、Cコンパイラがより多くのデッドコードを削除できることです)。

エミュレーションと翻訳の概念が理解されたら、動的にそれを試すことができますが、それは確かに簡単ではありません。バイナリをターゲットプロセッサのマシンコードに静的に変換してみることをお勧めします。これは良い練習です。バイナリに対して静的に実行することに成功するまで、動的実行時の最適化を試みませんでした。

仮想化は別の話です。同じプロセッサで同じプロセッサを実行することについて話しているのです。たとえば、x86上のx86です。ここでの利点は、古いx86プロセッサを使用しないと、仮想化されているプログラムを取得して、エミュレーションなしで実際のプロセッサで実際のオペコードを実行できることです。プロセッサに組み込まれたトラップを設定して処理を行うため、AXでの値の読み込みやBXの追加などはすべてプロセッサでリアルタイムに行われます。AXがメモリの読み取りまたは書き込みを行う場合、アドレスが範囲内にあるかどうかはトラップメカニズムによって異なります。仮想マシンはスペースをRAMし、トラップはありませんが、プログラムが仮想化されたUARTであるアドレスに書き込むとすると、プロセッサトラップがあり、それからvmwareまたはそれを書き込んでエミュレートするデコードが実際のシリアルポートと通信します。その1つの命令はリアルタイムではありませんでしたが、実行にはかなりの時間がかかりました。仮想化されたシリアルポートに値を書き込むその命令または一連の命令を置き換えてから、実際のシリアルポートまたは他の場所に書き込まれない可能性のある別のアドレスに書き込むことを選択した場合にできること障害が発生すると、VMマネージャーは命令をエミュレートする必要があります。または、トラップなしでuartへの書き込みを実行するコードを仮想メモリ空​​間に追加し、代わりにそのコードをこのuart書き込みルーチンに分岐させます。次にそのコードのチャンクに到達すると、リアルタイムで実行されるようになります。仮想化されたシリアルポートに値を書き込むその命令または一連の命令を置き換えてから、実際のシリアルポートまたは他の場所に書き込まれない可能性のある別のアドレスに書き込むことを選択した場合にできること障害が発生すると、VMマネージャーは命令をエミュレートする必要があります。または、トラップなしでuartへの書き込みを実行するコードを仮想メモリ空​​間に追加し、代わりにそのコードをこのuart書き込みルーチンに分岐させます。次にそのコードのチャンクに到達すると、リアルタイムで実行されるようになります。仮想化されたシリアルポートに値を書き込むその命令または一連の命令を置き換えてから、実際のシリアルポートまたは他の場所に書き込まれない可能性のある別のアドレスに書き込むことを選択した場合にできること障害が発生すると、VMマネージャーは命令をエミュレートする必要があります。または、トラップなしでuartへの書き込みを実行するコードを仮想メモリ空​​間に追加し、代わりにそのコードをこのuart書き込みルーチンに分岐させます。次にそのコードのチャンクに到達すると、リアルタイムで実行されるようになります。

たとえば、エミュレートして、llvmのような仮想中間バイトコードに変換することもできます。そこから、中間マシンからネイティブマシンに変換し、最終的にはプログラムの大部分を置き換えることができます。それでも周辺機器とI/Oに対処する必要があります。

于 2010-04-16T18:57:05.387 に答える
0

これは、「Rubinius」Rubyインタープテッターの動的再コンパイルをどのように行っているかについての説明です。

http://www.engineyard.com/blog/2010/making-ruby-fast-the-rubinius-jit/

于 2010-04-15T17:10:28.437 に答える
0

このアプローチは通常、中間バイトコード表現(Java、.netなど)を使用する環境で使用されます。バイトコードには十分な「高レベル」構造(マシンコードよりも高レベルの高レベル)が含まれているため、VMはバイトコードからチャンクを取り出して、コンパイルされたメモリブロックに置き換えることができます。コンパイル自体は複雑で時間のかかるプロセスであるため、VMは通常、コードがすでに解釈された回数をカウントすることによって、どの部分がコンパイルされるかを決定します。したがって、何度も実行される部分のみをコンパイルすると便利です。

しかし、どのようにしてオペコードを読み取り、そのコードを生成するのでしょうか。

オペコードのスキームはVMの仕様によって定義されるため、VMはプログラムファイルを開き、仕様に従って解釈します。

その人は事前に作成されたアセンブリチャンクを持っていて、それらを一緒にコピー/バッチ処理する必要がありますか?アセンブリはCで書かれていますか?

このプロセスはVMの実装の詳細であり、通常、VMオペコードストリームをマシンコードに変換できるコンパイラが組み込まれています。

システム割り込みをどのように説明しますか?

非常に単純です:なし。VM内のコードは、実際のハードウェアと対話できません。VMはOSと対話し、解釈されたコード内の特定の部分をジャンプ/呼び出して、OSイベントをコードに転送します。コード内またはOSからのすべてのイベントは、VMを通過する必要があります。

また、ハードウェア仮想化製品は、ある種のJITを使用できます。X86の世界での一般的な使用例は、16ビットのリアルモードコードを32ビットまたは64ビットのプロテクトモードコードに変換して、リアルモードでCPUをエミュレートするように強制されないようにすることです。また、ソフトウェアのみのVMは、実行中のコードのジャンプ命令をVM制御ソフトウェアにジャンプすることで置き換えます。VM制御ソフトウェアは、各分岐でジャンプ命令の次のコードパスをスキャンし、実際のコードの宛先にジャンプする前に置き換えます。しかし、ジャンプ置換がJITコンパイルとして適格であるかどうかは疑問です。

于 2010-04-16T07:26:03.633 に答える
-1

IISは、シャドウコピーによってこれを行います。コンパイル後、アセンブリを一時的な場所にコピーし、一時的な場所から実行します。

そのユーザーがいくつかのファイルを変更すると想像してみてください。次に、IISは次の手順でアセンブリを再コンパイルします。

  1. 再コンパイル(すべてのリクエストは古いコードで処理されます)
  2. 新しいアセンブリをコピーします(すべてのリクエストは古いコードで処理されます)
  3. すべての新しいリクエストは新しいコードで処理され、すべてのリクエストは古いコードで処理されます。

これがお役に立てば幸いです。

于 2010-04-09T13:18:47.607 に答える
-2

仮想マシンは、マシンコードではなく「バイトコード」または「中間言語」をロードするため、ランタイムデータが増えると、バイトコードをより効率的に再コンパイルするだけだと思います。

http://en.wikipedia.org/wiki/Just-in-time_compilation

于 2010-04-12T06:43:39.753 に答える