.NET言語のコンテキストでのマシンコードとネイティブコードについて混乱しています。
それらの違いは何ですか?彼らは同じですか?
.NET言語のコンテキストでのマシンコードとネイティブコードについて混乱しています。
それらの違いは何ですか?彼らは同じですか?
回答:
これらの用語は一貫性のない方法で使用されることがあるので、確かに少し混乱しています。
マシンコード:これは最も明確なものです。これは、プロセッサ(実際の作業を行う物理的な金属片)が直接理解して実行するバイトコード命令を使用するコードです。他のすべてのコードは、マシンで実行する前に、マシンコードに変換または変換する必要があります。
ネイティブコード:この用語は、マシンコード(上記を参照)を意味する場所で使用されることがあります。ただし、アンマネージコードを意味する場合もあります(下記参照)。
アンマネージコードとマネージコード: アンマネージコードとは、CやC ++などのプログラミング言語で書かれたコードを指し、マシンコードに直接コンパイルされます。これは、C#、VB.NET、Javaなどで記述され、ソフトウェアでプロセッサを「シミュレート」する仮想環境(.NETやJavaVMなど)で実行されるマネージコードとは対照的です。主な違いは、ガベージコレクションを採用し、オブジェクトへの参照を不透明に保つことで、マネージコードがリソース(主にメモリ割り当て)を「管理」することです。アンマネージコード手動でメモリを割り当てたり割り当て解除したりする必要がある種類のコードで、メモリリーク(割り当て解除を忘れた場合)や、セグメンテーションエラー(割り当て解除が早すぎる場合)が発生する場合があります。アンマネージは、通常、nullポインターの逆参照や配列の境界のオーバーフローなどの一般的なエラーのランタイムチェックがないことも意味します。
厳密に言えば、Perl、Python、PHP、Rubyなどのほとんどの動的型付け言語もマネージコードです。ただし、それらは一般的にはそのように記述されていないため、マネージコードは、実際には非常に大きく、本格的な商用プログラミング環境(.NETおよびJava)のマーケティング用語の一部であることを示しています。
アセンブリコード:この用語は一般に、バイトコードを本当に書きたいときに人々が書くソースコードの種類を指します。アセンブラは、実際のバイトコードにこのソースコードをオンにするプログラムです。変換は1対1であるため、コンパイラーではありません。ただし、使用されるバイトコードの種類については、あいまいな用語であり、管理されている場合と管理されていない場合があります。管理されていない場合、結果のバイトコードはマシンコードになります。管理されている場合は、.NETなどの仮想環境によって舞台裏でバイトコードが使用されます。マネージコード(C#、Javaなど)は、この特殊なバイトコード言語にコンパイルされます。これは、.NETの場合はCommon Intermediate Language(CIL)と呼ばれ、JavaではJavaバイトコードと呼ばれます。。通常、一般のプログラマーがこのコードにアクセスしたり、この言語で直接書き込んだりする必要はほとんどありませんが、アセンブラーを使用してバイトコードに変換するため、人々がアクセスする場合、アセンブリコードと呼ばれることがよくあります。
C#プログラムのデバッグ時にDebug + Windows + Disassemblyを使用したときに表示される内容は、これらの用語の良いガイドです。JIT最適化を有効にしたリリース構成でC#で記述された 'hello world'プログラムをコンパイルしたときの注釈付きバージョンは次のとおりです。
static void Main(string[] args) {
Console.WriteLine("Hello world");
00000000 55 push ebp ; save stack frame pointer
00000001 8B EC mov ebp,esp ; setup current frame
00000003 E8 30 BE 03 6F call 6F03BE38 ; Console.Out property getter
00000008 8B C8 mov ecx,eax ; setup "this"
0000000a 8B 15 88 20 BD 02 mov edx,dword ptr ds:[02BD2088h] ; arg = "Hello world"
00000010 8B 01 mov eax,dword ptr [ecx] ; TextWriter reference
00000012 FF 90 D8 00 00 00 call dword ptr [eax+000000D8h] ; TextWriter.WriteLine()
00000018 5D pop ebp ; restore stack frame pointer
}
00000019 C3 ret ; done, return
ウィンドウを右クリックして[コードバイトの表示]を選択すると、同様の表示が得られます。
左側の列はマシンコードアドレスです。その値はデバッガによって偽造され、コードは実際には別の場所に配置されます。しかし、JITコンパイラによって選択された場所によっては、どこにでもある可能性があるため、デバッガはメソッドの開始時にアドレスの番号を0から開始するだけです。
2列目はマシンコードです。CPUが実行する実際の1および0。ここと同様に、マシンコードは通常16進数で表示されます。実例としては、0x8BがMOV命令を選択することであり、追加のバイトは、移動する必要があるものを正確にCPUに伝えるために存在します。また、CALL命令には2つの種類があります。0xE8は直接呼び出し、0xFFは間接呼び出し命令です。
3番目の列はアセンブリコードです。アセンブリは、マシンコードを簡単に記述できるように設計された単純な言語です。ILにコンパイルされているC#と比較します。アセンブリコードの変換に使用されるコンパイラは、「アセンブラ」と呼ばれます。おそらくマシンにMicrosoftアセンブラーがあり、その実行可能ファイル名はml.exe、64ビット版のml64.exeです。使用されているアセンブリ言語の2つの一般的なバージョンがあります。表示されるのは、IntelとAMDが使用しているものです。オープンソースの世界では、AT&T表記でのアセンブリが一般的です。言語構文は、作成されたCPUの種類に大きく依存します。PowerPCのアセンブリ言語は大きく異なります。
さて、それはあなたの質問の2つの用語に取り組みます。「ネイティブコード」はあいまいな用語であり、アンマネージ言語でコードを記述するために一般的に使用されることはありません。有益なのは、Cコンパイラによってどのような種類のマシンコードが生成されるかを確認することです。これはCの「hello world」バージョンです。
int _tmain(int argc, _TCHAR* argv[])
{
00401010 55 push ebp
00401011 8B EC mov ebp,esp
printf("Hello world");
00401013 68 6C 6C 45 00 push offset ___xt_z+128h (456C6Ch)
00401018 E8 13 00 00 00 call printf (401030h)
0040101D 83 C4 04 add esp,4
return 0;
00401020 33 C0 xor eax,eax
}
00401022 5D pop ebp
00401023 C3 ret
注釈を付けなかったのは、主にC#プログラムによって生成されたマシンコードに非常に似ているためです。printf()関数呼び出しはConsole.WriteLine()呼び出しとはかなり異なりますが、その他はすべて同じです。また、デバッガーが実際のマシンコードアドレスを生成し、シンボルについては少し賢くなっていることにも注意してください。アンマネージコンパイラのように、マシンコードを生成した後にデバッグ情報を生成することの副作用は、しばしば発生します。また、マシンコードを似たものにするために、いくつかのマシンコード最適化オプションをオフにしたことにも言及する必要があります。C / C ++コンパイラーは、コードを最適化するためにより多くの時間を利用できますが、その結果はしばしば解釈が困難です。そして、非常にデバッグが難しいです。
ここで重要な点はあるある非常にネイティブコードコンパイラによって生成されたJITコンパイラとマシンコードで管理される言語から生成されたマシンコードの間にいくつかの違いが。これが、C#言語がネイティブコードコンパイラと競合できる主な理由です。それらの間の唯一の本当の違いは、サポート関数呼び出しです。その多くはCLRに実装されています。そして、それはガベージコレクターを中心に展開します。