2010-08-08 14 views
73

Estoy confundido acerca de código de máquina y código nativo.¿Cuál es la diferencia entre el código nativo, el código de máquina y el código de ensamblaje?

¿Cuál es la diferencia entre ellos? ¿Son lo mismo?

+1

Tengo una pregunta con respecto a esta pregunta. ¿Esta pregunta cae bajo el requisito de StackOverflow? afaik no lo es, pero al mismo tiempo este tipo de pregunta es muy útil/informativo. Suponiendo que este tipo de pregunta no está permitida, ¿dónde deberíamos hacer este tipo de preguntas si no está aquí? – sami1592

Respuesta

2

En .NET, los conjuntos contienen MS Intermediate Language código (MSIL, a veces CIL).
Es como un código de máquina de "alto nivel".

Cuando se carga, MSIL se compila por el JIT compiler en el código nativo (código de máquina Intel x86 o x64).

5

El código nativo y el código máquina son lo mismo: los bytes reales que ejecuta la CPU.

El código de ensamblado tiene dos significados: uno es el código de máquina traducido a una forma más humana (con los bytes de las instrucciones traducidas en mnemotécnicos cortos como "JMP" (que "salta" a otro lugar del código)). El otro es el bytecode IL (bytes de instrucción que los compiladores como C# o VB generan, que terminarán traducidos a código de máquina con el tiempo, pero todavía no) que vive en un DLL o EXE.

113

Los términos son de hecho un poco confuso, porque a veces se usan de manera inconsistente.

Código máquina: Este es el código más definido. es las instrucciones de código de bytes que su procesador (la pieza de metal física que hace el trabajo real) entiende y ejecuta directamente. El resto del código debe traducirse o transformarse en código máquina antes de que su máquina pueda ejecutarlo.

código nativo: Este término se utiliza a veces en lugares donde el código máquina (véase más arriba) se significa. Sin embargo, también se usa a veces para indicar código no administrado (ver a continuación).

código no administrado y código administrado:Unmanaged código se refiere a código escrito en un lenguaje de programación tal como C o C++, que se compila directamente en código máquina . Contrasta con código administrado, que está escrito en C#, VB.NET, Java o similar, y se ejecuta en un entorno virtual (como .NET o JavaVM) que tipo de "simula" un procesador en el software. La principal diferencia es que código administrado "gestiona" los recursos (principalmente la asignación de memoria) para usted mediante el uso de recolección de basura y al mantener las referencias a los objetos opacos. El código no administrado es el tipo de código que requiere que asignes y desasignes manualmente la memoria, lo que a veces causa pérdidas de memoria (cuando te olvidas de desasignar) y, a veces, fallas de segmentación (cuando te desasignas demasiado pronto). Unmanaged también suele implicar que no hay comprobaciones en tiempo de ejecución para errores comunes como desreferenciación de punteros nulos o desbordamiento de límites de matriz.

Estrictamente hablando, la mayoría de los lenguajes de tipo dinámico, como Perl, Python, PHP y Ruby, también son código administrado. Sin embargo, no se describen comúnmente como tales, lo que demuestra que código administrado es en realidad un término comercial para los entornos de programación comercial realmente grandes, serios (.NET y Java).

Código de ensamblaje: Este término generalmente se refiere al tipo de código fuente que las personas escriben cuando realmente desean escribir un código de bytes. Un ensamblador es un programa que convierte este código fuente en código de bytes real. No es un compilador porque la transformación es 1-a-1. Sin embargo, el término es ambiguo en cuanto a qué tipo de código de bytes se usa: podría ser administrado o no. Si no se gestiona, el código de bytes resultante es código máquina. Si se administra, resulta en el código de bytes utilizado detrás de las escenas por un entorno virtual como .NET. El código administrado (por ejemplo, C#, Java) se compila en este lenguaje especial de código de bytes, que en el caso de .NET se llama Lenguaje Intermedio Común (CIL) y en Java se llama Java byte-code. Por lo general, el programador común tiene poca necesidad de acceder a este código o de escribir directamente en este idioma, pero cuando las personas lo hacen, a menudo lo llaman como código de ensamblaje porque usan un ensamblador para convertirlo en byte-code .

+0

C++ puede compilar código máquina, pero a menudo se compila a otros formatos como exe que se ejecutará con un sistema operativo. –

+0

Hay idiomas que admiten la recolección de elementos no utilizados y referencias opacas que normalmente se compilan en código máquina. Las implementaciones más serias de Common Lisp hacen eso. Lo que diga puede ser cierto para los lenguajes compatibles con Microsoft, pero hay más lenguajes compilados de los que admite Visual Studio. –

+1

@CrazyJugglerDrummer: El código contenido en los archivos EXE generados por los compiladores de C++ sigue siendo código de máquina. @David Thornley: mencioné significativamente más idiomas que solo esos, pero no quería complicar las cosas mencionando cada rareza oscura. – Timwi

38

Lo que ve cuando usa Debug + Windows + Desmontaje al depurar un programa C# es una buena guía para estos términos. Aquí está una versión comentada de que cuando compilo un programa 'hola mundo', escrito en C# en la configuración de lanzamiento con la optimización de JIT habilitado:

 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 

Haga clic derecho en la ventana y marque la opción "Mostrar código de bytes" para obtener una exhibición similar.

La columna de la izquierda es la dirección del código de máquina. Su valor es falsificado por el depurador, el código está ubicado en otro lugar. Pero eso podría estar en cualquier lugar, dependiendo de la ubicación seleccionada por el compilador JIT, por lo que el depurador simplemente comienza a numerar las direcciones desde 0 al comienzo del método.

La segunda columna es el código de máquina . Los 1s y 0s reales que ejecuta la CPU. El código de máquina, como aquí, se muestra comúnmente en hexadecimal. Quizás lo más ilustrativo sea que 0x8B selecciona la instrucción MOV, los bytes adicionales están ahí para decirle a la CPU exactamente lo que se debe mover. También tenga en cuenta los dos sabores de la instrucción CALL, 0xE8 es la llamada directa, 0xFF es la instrucción de llamada indirecta.

La tercera columna es código de ensamblaje. Assembly es un lenguaje simple, diseñado para facilitar la escritura de código de máquina. Se compara con C# compilado a IL. El compilador utilizado para traducir el código ensamblador se denomina "ensamblador". Probablemente tenga el ensamblador de Microsoft en su máquina, su nombre ejecutable es ml.exe, ml64.exe para la versión de 64 bits. Hay dos versiones comunes de lenguajes de ensamblaje en uso. El que ves es el que usan Intel y AMD. En el mundo de fuente abierta, el ensamblado en la notación T AT & es común. La sintaxis del lenguaje depende en gran medida del tipo de CPU para el que se escribió, el lenguaje ensamblador para un PowerPC es muy diferente.

De acuerdo, eso aborda dos de los términos en su pregunta. El "código nativo" es un término difuso, no se usa poco para describir el código en un lenguaje no administrado. Quizás sea instructivo ver qué tipo de código máquina es generado por un compilador de C. Esta es la versión del 'hola mundo' en C:

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 

yo no anotarla, sobre todo porque es tan similares al código máquina generado por el programa C#. La llamada a la función printf() es bastante diferente de la llamada Console.WriteLine() pero todo lo demás es más o menos lo mismo.También tenga en cuenta que el depurador ahora está generando la dirección del código de máquina real y que es un poco más inteligente con respecto a los símbolos. Un efecto secundario de generar información de depuración después de generar código de máquina como los compiladores no administrados a menudo lo hacen. También debo mencionar que desactivé algunas opciones de optimización de código de máquina para hacer que el código de la máquina se vea similar. Los compiladores C/C++ tienen mucho más tiempo disponible para optimizar el código, el resultado a menudo es difícil de interpretar. Y muy difícil de depurar.

El punto clave es que hay muy pocas diferencias entre el código máquina generado a partir de un lenguaje administrado por el compilador JIT y el código máquina generado por un compilador de código nativo. Cuál es la razón principal por la cual el lenguaje C# puede ser competitivo con un compilador de código nativo. La única diferencia real entre ellos son las llamadas de función de soporte. Muchos de los cuales se implementan en el CLR. Y eso gira alrededor del recolector de basura.

Cuestiones relacionadas