Estoy confundido sobre el código máquina y el código nativo en el contexto de los lenguajes .NET.
¿Cuál es la diferencia entre ellos? ¿Son lo mismo?
Estoy confundido sobre el código máquina y el código nativo en el contexto de los lenguajes .NET.
¿Cuál es la diferencia entre ellos? ¿Son lo mismo?
Respuestas:
De hecho, los términos son un poco confusos, porque a veces se usan de manera inconsistente.
Código de máquina: este es el mejor definido. Es un código que usa las instrucciones de código de bytes que su procesador (la pieza física de metal que hace el trabajo real) comprende y ejecuta directamente. El resto del código debe traducirse o transformarse en código de máquina antes de que su máquina pueda ejecutarlo.
Código nativo: este término se usa a veces en lugares donde se hace referencia al código de máquina (ver arriba). Sin embargo, a veces también se usa para referirse a código no administrado (ver más abajo).
Código no administrado y código administrado: el código no administrado se refiere al código escrito en un lenguaje de programación como C o C ++, que se compila directamente en código de máquina . Contrasta con el 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 “simula” un procesador en software. La principal diferencia es que el código administrado "administra" los recursos (principalmente la asignación de memoria) por usted empleando la recolección de basura y manteniendo opacas las referencias a los objetos. Código no administradoes el tipo de código que requiere que asigne y desasigne memoria manualmente, lo que a veces provoca pérdidas de memoria (cuando se olvida de desasignar) y, a veces, fallas de segmentación (cuando desasigna demasiado pronto). No administrado también suele implicar que no hay comprobaciones en tiempo de ejecución de errores comunes, como desreferenciación de puntero nulo o desbordamiento de límites de matriz.
Estrictamente hablando, la mayoría de los lenguajes de tipado 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 muestra que el código administrado es en realidad un término de marketing para los entornos de programación comercial realmente grandes y serios (.NET y Java).
Código ensamblador: este término generalmente se refiere al tipo de código fuente que la gente escribe cuando realmente quiere escribir 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 administrado. Si no está administrado, el código de bytes resultante es código de máquina . Si se administra, el código de bytes se usa detrás de escena por un entorno virtual como .NET. El código administrado (por ejemplo, C #, Java) se compila en este lenguaje de código de bytes especial, que en el caso de .NET se denomina Lenguaje Intermedio Común (CIL) y en Java se denomina código de bytes de Java.. Por lo general, el programador común tiene poca necesidad de acceder a este código o escribir en este lenguaje directamente, pero cuando la gente lo hace, a menudo se refiere a él como código ensamblador porque usa un ensamblador para convertirlo en código de bytes.
Lo que ve cuando usa Depurar + Windows + Desmontaje al depurar un programa C # es una buena guía para estos términos. Aquí hay una versión anotada de la misma cuando compilo un programa 'hola mundo' escrito en C # en la configuración de lanzamiento con la optimización JIT habilitada:
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 con el botón derecho en la ventana y marque "Mostrar bytes de código" para obtener una pantalla similar.
La columna de la izquierda es la dirección del código de la máquina. Su valor es falsificado por el depurador, el código está ubicado en otro lugar. Pero eso podría ser en cualquier lugar, dependiendo de la ubicación seleccionada por el compilador JIT, por lo que el depurador simplemente comienza a numerar direcciones desde 0 al comienzo del método.
La segunda columna es el código de la máquina . Los 1 y 0 reales que ejecuta la CPU. El código de máquina, como aquí, se muestra comúnmente en hexadecimal. Quizás ilustrativo es que 0x8B selecciona la instrucción MOV, los bytes adicionales están ahí para decirle a la CPU exactamente lo que necesita moverse. También tenga en cuenta los dos tipos de la instrucción CALL, 0xE8 es la llamada directa, 0xFF es la instrucción de llamada indirecta.
La tercera columna es el código ensamblador . El ensamblaje es un lenguaje simple, diseñado para facilitar la escritura de código de máquina. Se compara con la compilación de C # en IL. El compilador utilizado para traducir el código ensamblador se llama "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 ensambladores en uso. El que ves es el que usan Intel y AMD. En el mundo del código abierto, el ensamblaje en la notación de AT&T es común. La sintaxis del lenguaje depende en gran medida del tipo de CPU para el que se escribió, el lenguaje ensamblador de un PowerPC es muy diferente.
Bien, eso aborda dos de los términos de su pregunta. El "código nativo" es un término confuso, no es infrecuente que se utilice para describir código en un lenguaje no administrado. Quizás sea instructivo ver qué tipo de código de máquina genera un compilador de C. Esta es la versión '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
No lo anoté, principalmente porque es muy similar al código de 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 casi igual. 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 los símbolos. Un efecto secundario de generar información de depuración después de generar código de máquina, como suelen hacer los compiladores no administrados. También debo mencionar que desactivé algunas opciones de optimización de código de máquina para que el código de máquina se vea similar. Los compiladores de C / C ++ tienen mucho más tiempo disponible para optimizar el código, el resultado es a menudo difícil de interpretar. Y muy difícil de depurar.
El punto clave aquí es que existen 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. Esta es la razón principal por la que el lenguaje C # puede competir con un compilador de código nativo. La única diferencia real entre ellos son las llamadas a la función de soporte. Muchos de los cuales se implementan en CLR. Y eso gira principalmente en torno al recolector de basura.
El código nativo y el código de máquina son lo mismo: los bytes reales que ejecuta la CPU.
El código ensamblador tiene dos significados: uno es el código de máquina traducido a una forma más legible por humanos (con los bytes de las instrucciones traducidos a mnemónicos cortos parecidos a palabras como "JMP" (que "salta" a otro lugar en el código). El otro es el código de bytes IL (bytes de instrucción que generan compiladores como C # o VB, que eventualmente terminarán traducidos al código de máquina, pero que aún no lo están) que vive en una DLL o EXE.
En .NET, los ensamblados contienen código de lenguaje intermedio de MS (MSIL, a veces CIL).
Es como un código de máquina de "alto nivel".
Cuando se carga, MSIL es compilado por el compilador JIT en código nativo (código de máquina Intel x86 o x64).