本机代码,机器代码和汇编代码之间有什么区别?


106

我对.NET语言上下文中的机器代码和本机代码感到困惑。

它们之间有什么区别?他们是一样的吗?


3
我对此有一个疑问。这个问题是否属于StackOverflow的要求?并非如此,但与此同时,此类问题也非常有帮助/提供信息。假设不允许这种类型的问题,如果这里不允许我们在哪里问这些类型的问题?
Yousuf Azad'2

Answers:


150

这些术语确实有点令人困惑,因为它们有时会不一致地使用。

机器代码:这是定义最明确的代码。它是使用您的处理器(执行实际工作的物理金属)理解并直接执行的字节码指令的代码。在机器可以执行之前,必须将所有其他代码转换或转换为机器代码

本机代码:该术语有时用于表示机器代码(见上文)的地方。但是,有时也用于表示非托管代码(请参见下文)。

非托管代码托管代码: 非托管代码是指以诸如C或C ++之类的编程语言编写的代码,这些代码直接编译为机器代码。它与用C#,VB.NET,Java或类似语言编写并在虚拟环境(例如.NET或JavaVM)中执行的托管代码形成对比,托管环境在某种程度上“模拟”软件中的处理器。主要区别在于,托管代码通过使用垃圾回收并使对象的引用保持不透明来为您“管理”资源(主要是内存分配)。非托管代码这类代码需要您手动分配和取消分配内存,有时会导致内存泄漏(当您忘记取消分配时),有时会导致分段错误(当您取消分配得太早时)。非托管通常也意味着没有运行时检查常见错误,例如空指针解引用或数组边界溢出。

严格来说,大多数动态类型化的语言(例如Perl,Python,PHP和Ruby)也是托管代码。但是,它们并不是通常这样描述的,这表明托管代码实际上对于真正的大型,严肃的商业编程环境(.NET和Java)而言实际上是一个营销术语。

汇编代码:该术语通常是指人们真正想写字节码时所写的源代码。一个汇编程序是一个程序,打开这个源代码转换成真正的字节码。它不是编译器,因为转换是一对一的。但是,该术语对于使用哪种字节码是模棱两可的:它可以是托管的也可以是非托管的。如果不受管,则生成的字节码为机器码。如果进行管理,它将导致虚拟环境(如.NET)在幕后使用字节码。托管代码(例如C#,Java)被编译成这种特殊的字节码语言,在.NET中,这种语言称为通用中间语言(CIL),在Java中称为Java字节码。。通常,普通程序员几乎不需要访问该代码或直接用这种语言编写,但是当人们这样做时,他们通常将其称为汇编代码,因为他们使用汇编器将其转换为字节码。


C ++可以编译为机器代码,但通常会编译为其他格式,例如将与操作系统一起运行的exe。
戈登·古斯塔夫森

有些语言支持垃圾回收和不透明的引用,这些引用通常编译为机器代码。Common Lisp的最严肃的实现可以做到这一点。您所说的可能适用于Microsoft支持的语言,但是编译后的语言比Visual Studio支持的语言更多。
David Thornley,2010年

3
@CrazyJugglerDrummer:C ++编译器生成的EXE文件中包含的代码仍然是机器代码。@David Thornley:我提到的语言远远超过其他语言,但我不想通过提及每个晦涩难懂的怪事来使事情复杂化。
Timwi

一些编译器实际上会从C / C ++或其他语言编译为汇编语言,然后调用汇编器,然后汇编器将其转换为目标文件,这些文件主要是机器代码,但需要一些操作才能进入处理器的内存,然后链接器将所有链接链接到程序的机器代码版本。C / C ++等问题通常无法直接编译为机器代码,而用户看不见的代码则需要两三个步骤。例如,TCC是一个例外,它确实直接用于机器代码。
old_timer 2010年

感觉就像挑剔,但并非所有汇编程序都将1-1转换为操作码。实际上,许多现代汇编程序都支持类之类的抽象构造。示例:TASM,Borland的汇编程序。 en.wikipedia.org/wiki/TASM
总理

45

在调试C#程序时使用Debug + Windows + Disassembly时看到的内容是这些术语的不错指南。这是在启用JIT优化的情况下,在Release配置中编译用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开始对地址编号。

第二列是机器代码。CPU执行的实际1和0。像这里一样,机器代码通常以十六进制显示。说明性的也许是0x8B选择了MOV指令,附加字节在那里告诉CPU到底需要移动什么。还要注意CALL指令的两种形式,0xE8是直接调用,0xFF是间接调用指令。

第三列是汇编代码。汇编是一种简单的语言,旨在使编写机器代码更加容易。相比之下,将C#编译为IL。用于翻译汇编代码的编译器称为“汇编器”。您的计算机上可能装有Microsoft汇编器,其可执行文件名为ml.exe,64位版本为ml64.exe。有两种常用的汇编语言版本。您看到的是Intel和AMD使用的一种。在开源世界中,以AT&T表示法进行组装是很常见的。语言语法在很大程度上取决于为其编写的CPU的类型,而PowerPC的汇编语言则大不相同。

好的,这解决了您的问题中的两个词。“本机代码”是一个模糊的术语,它通常用于以非托管语言描述代码。很有启发性的是看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中实现的。这主要围绕垃圾收集器展开。


6

本机代码和机器代码是一回事-CPU执行的实际字节。

汇编代码具有两种含义:一种是将机器代码转换为更易于理解的形式(将指令的字节转换为类似“ JMP”的简短单词式助记符(“跳转”到代码中的另一个位置)。是位于DLL或EXE中的IL字节码(由C#或VB这样的编译器生成的指令字节,最终将最终转换为机器代码,但尚未被翻译成机器代码)。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.