válido significado que programa net msil microsoft language intermediate framework detectó common .net clr vm-implementation

.net - significado - que es msil microsoft intermediate language



¿Es el CLR una máquina virtual? (7)

He leído un libro que se refiere al .net CLR como una máquina virtual ? ¿Alguien puede justificar esto? ¿Cuál es la razón por la que necesitamos el concepto de máquinas virtuales en algunas plataformas de desarrollo?

¿No es posible desarrollar un marco nativo [uno sin máquina virtual] que esté completamente orientado a objetos y tan poderoso como .net?

El libro que hace referencia a CLR como máquina virtual es " Professional .Net Framework 2.0 ".


Hay muchos conceptos erróneos aquí. Supongo que podría pensar en .Net como una máquina virtual si realmente quisiera, pero veamos cómo el .Net Framework realmente maneja su código. El escenario típico se ve así.

  1. Usted escribe un programa .Net en C #, VB.Net, F # o algún otro lenguaje compatible.
  2. Ese código se compila en un lenguaje intermedio (IL), que es similar al código de bytes de Java, que se distribuye a las máquinas de los usuarios finales.
  3. Un usuario final invoca el programa por primera vez en una computadora con la versión correcta de .Net instalada
  4. La computadora ve que esto es un ensamblado .Net en lugar de un código de máquina "sin procesar", y lo pasa al compilador JIT
  5. El compilador JIT compila el IL a un código de máquina completamente nativo .
  6. El código nativo se guarda en la memoria durante la ejecución de este programa.
  7. Se invoca el código nativo guardado y el IL ya no importa.

Hay un par de puntos importantes aquí, pero el más importante es que en ningún momento se interpreta ningún código. En su lugar, puede ver en el paso 5 que está compilado en código nativo. Esta es una gran diferencia que cargarla en una máquina virtual, por varias razones:

  1. El código completamente compilado es ejecutado por la CPU directamente en lugar de ser interpretado o traducido por una capa de abstracción de software adicional, que debería ser más rápida.
  2. El compilador JIT puede aprovechar las optimizaciones específicas de la máquina individual que ejecuta el programa, en lugar de conformarse con un mínimo común denominador.
  3. Si lo desea, puede incluso compilar previamente el código y, en esencia, ocultar el paso 5 al usuario por completo.

Supongo que podría llamarse a esto una máquina virtual, en el sentido de que el JITter abstrae los detalles de la máquina real del desarrollador. Personalmente, no creo que eso sea realmente correcto, porque para muchas personas, una máquina virtual implica una abstracción en tiempo de ejecución lejos del código nativo que para los programas .Net simplemente no existe.

Otro punto clave sobre todo este proceso que realmente lo distingue de un entorno de "máquina virtual" es que es solo el proceso típico . Si realmente lo desea, puede precompilar un ensamblaje .Net antes de la distribución e implementar código nativo directamente para los usuarios finales (sugerencia: es más lento en conjunto durante la vida del programa, ya que pierde optimizaciones específicas de la máquina). Por supuesto, aún necesita que se instale .Net runtime, pero en este punto no es muy diferente de cualquier otra API de tiempo de ejecución; es más como una colección de dlls con una buena API con la que se puede vincular, como podría tener con los tiempos de ejecución VB o C que Microsoft también incluye con Visual Studio. Este tipo de toma el IL de la imagen, lo que hace que el moniker VM sea mucho más difícil de justificar. (Digo "algo así" porque el IL todavía se implementa y se usa para verificar el código guardado, pero nunca se toca para la ejecución).

Otro punto revelador es la falta de un proceso de VM. Cuando ejecuta su aplicación, no hay un proceso común de "caja de arena" que se ejecute. Compare esto con Java, donde si abre el administrador de tareas cuando se está ejecutando un programa, verá un proceso específicamente para la VM de Java, y el proceso real de la aplicación es un hilo dentro del recinto de seguridad creado por la VM. En .Net, puede ver el proceso de la aplicación directamente en el administrador de tareas de Windows.

En resumen: se podría decir que IL + CLR + JIT de alguna manera conforman una máquina virtual. Personalmente no lo creo, pero no discutiré contigo si crees eso. El punto que quiero señalar es que cuando le dice a alguien que .Net se ejecuta en una máquina virtual sin ninguna otra explicación, la idea que le está comunicando a esa persona es "código de bytes interpretado en un proceso de host". Y eso está mal.


La parte de "Máquina virtual" se refiere al hecho de que el código .NET se compila en EXE y DLL como lenguaje de ensamblaje "Intermedio" (IL) para ejecutarse en una máquina virtual, a diferencia del lenguaje de ensamblaje de CPU real. Luego, en tiempo de ejecución, el ILM se convierte en un ensamblaje de CPU real para su ejecución (denominado Just-in-time o compilación JIT).

Claro, puede escribir un compilador .NET para que se compile en lenguaje ensamblador de CPU en lugar de IL. Sin embargo, esto no sería portátil para todas las CPU, tendría que compilar una versión diferente para cada par de SO / CPU. Pero al compilar en ILM, deja que la "máquina virtual" maneje la CPU y las cosas específicas del sistema operativo.


La ventaja de CLR es la libertad de escribir código en cualquier lenguaje de programación que el desarrollador elija, ya que el código se compilará en CLR antes de ser interpretado en llamadas nativas. .NET Framework utiliza esta compilación JIT para tratar todo de manera uniforme y genera programas que funcionan para la plataforma que se está implementando, que está ausente en los lenguajes compilados.


Ni la JVM ni la CLR hacen nada que sea sustancialmente diferente de lo que la mayoría de las "máquinas virtuales" para otros idiomas también hacen. Modernamente, todos usan JIT para convertir instrucciones virtuales (código p, códigos de bytes, instrucciones de lenguaje intermedio, llámenlo como desee) a instrucciones de "hardware de CPU nativo" ("código de máquina").

De hecho, la primera "máquina virtual" para hacer esto fue la máquina virtual Smalltalk. El autor de esa innovación, Peter Deutsch, la llamó "traducción dinámica" en lugar del término "JIT", que fue popularizado por Java. Si el "entorno de ejecución del tiempo de ejecución" de Smalltalk se va a llamar "máquina virtual" (y así se llama,), todos y cada uno de los demás "sistemas de tiempo de ejecución" que hacen lo mismo también califican como "máquinas virtuales". "


Similar a la máquina virtual de Java (JVM), el .net CLR es una máquina virtual de interpretación de códigos de bytes.

La JVM interpreta los programas que contienen códigos de byte java y el CLR .net interpreta los programas que contienen lo que Microsoft llama instrucciones de "lenguaje intermedio (IL)". Existen diferencias entre estos códigos de bytes, pero las máquinas virtuales son similares y aspiran a proporcionar características similares.

Ambas implementaciones de máquinas virtuales tienen la capacidad de compilar su código de bytes de entrada al lenguaje de máquina del equipo en el que se ejecutan. Esto se llama "Just In Time Compilation (JIT)" y el código de salida producido se llama "JIT code". Debido a que el código JIT contiene secuencias de instrucciones en el lenguaje de máquina de la CPU de la computadora, este código a veces se denomina código "nativo".

Sin embargo, el código JIT es cualitativamente y cuantitativamente diferente del código nativo, como se explica a continuación. Por esa razón, este artículo considera que el código JIT no es más que una implementación nativa de la Máquina Virtual mientras se ejecuta un programa de bytecode en particular .

Una característica que ambas máquinas virtuales (VM) aspiran a proporcionar es la seguridad en la forma de prevenir ciertos errores de programación peligrosos. Por ejemplo, el título de este foro del sitio web, , está inspirado en uno de estos tipos de error peligroso que es posible en el código nativo.

Para proporcionar seguridad y seguridad de ejecución, las máquinas virtuales implementan el tipo de seguridad en el "nivel de máquina virtual". Las asignaciones a la memoria de la VM son necesarias para almacenar el tipo de datos que se guardan en esa ubicación de la memoria. Por ejemplo, si se empuja un entero en la pila, no es posible extraer un doble de la pila. Las "uniones" de estilo C están prohibidas. Los punteros y el acceso directo a la memoria están prohibidos.

No podríamos obtener los mismos beneficios al imponer un marco de lenguaje orientado a objetos en los desarrolladores si el resultado es un binario nativo como un archivo EXE. En ese caso, no podríamos distinguir entre binarios nativos generados utilizando el marco y EXE generados por un usuario malintencionado que emplea fuentes distintas al marco.

En el caso de las máquinas virtuales, la seguridad de tipo se aplica en el "nivel más bajo" al que el programador puede acceder. (Dejando por un momento que sea posible escribir código nativo administrado, es decir). Por lo tanto, ningún usuario encontrará una aplicación que realice una de las operaciones peligrosas que requieren acceso directo a las ubicaciones de memoria y los punteros.

En la práctica, el .net CLR implementa una forma de escribir código nativo que puede llamarse mediante el código "administrado" .net. En este caso, la carga recae en el autor del código nativo para no cometer errores de puntero y memoria.

Como JVM y .net CLR realizan la compilación JIT, cualquiera de las máquinas virtuales crea un binario compilado de forma nativa a partir del código de bytes proporcionado. Este "código JIT" funciona más rápido que la ejecución del intérprete de la máquina virtual, ya que incluso el código en lenguaje de máquina producido por JIT contiene todas las comprobaciones de seguridad necesarias de la máquina virtual que la máquina virtual realizaría. Como resultado, el código de salida JIT no es tan rápido como el código nativo que normalmente no contendría numerosas verificaciones en tiempo de ejecución. Sin embargo, este inconveniente en el rendimiento de la velocidad se intercambia para mejorar la confiabilidad, incluida la seguridad; en particular, se evita el uso de almacenamiento no inicializado, se impone la seguridad de tipo de las asignaciones, se realiza la verificación de rangos (por lo tanto se evitan los desbordamientos de búfer basados ​​en pila y pila), la vida útil de los objetos se gestiona mediante la recolección de basura, la asignación dinámica es segura. Un entorno que ejecuta tales verificaciones de comportamiento en tiempo de ejecución está implementando la especificación de una máquina virtual y es poco más que una realización en lenguaje de máquina de una máquina virtual.


Soy un poco viejo, así que también llamo a la CLR una máquina virtual. Mi razonamiento es que el CLR ensambla el código de la máquina a partir de un bytecode intermedio, que es lo que también hace una máquina virtual.

Los beneficios del CLR se deben principalmente a la forma en que ensambla el código de máquina que utiliza información de tipo de tiempo de ejecución.

Puede desarrollar un marco nativo tan poderoso como el marco .NET usando solo tipos nativos. La única flexibilidad que pierde es la capacidad de volver a ensamblar el código nativo si alguna vez transporta su programa a otra plataforma sin recompilar.


Tienes muchas respuestas valiosas, pero creo que una cosa aún no se ha mencionado: la modularidad.

Es bastante difícil exportar una clase OO desde DLL nativo. Claro, puedes decirle al enlazador que exporte la clase e importe en otro lugar, pero esto es frágil; Cambiar un solo miembro privado en una clase romperá la compatibilidad binaria, es decir, si cambia una DLL sin volver a compilar todos los otros módulos, su programa se bloqueará horriblemente en el tiempo de ejecución.

Hay algunas formas de solucionar esto: por ejemplo, puede definir interfaces abstractas públicas, derivar de ellas y exportar funciones de fábrica globales desde su DLL. De esa manera, puede cambiar los detalles de implementación de una clase. Pero no puedes derivar de esa clase en otra DLL. Y cambiar la interfaz también rompe la compatibilidad binaria, por supuesto.

No estoy seguro de si existe una buena solución para esto en el código nativo: si el compilador / enlazador crea código nativo en el momento de la compilación, entonces debe conocer el diseño de memoria exacto de las clases / estructuras que se usan en el código. Si el último paso de compilación (generar código nativo) se retrasa hasta que se llama a un método por primera vez, este problema simplemente desaparece: puede modificar una clase en un ensamblaje, y siempre que el JIT pueda resolver todos los miembros usados en tiempo de ejecución , todo funcionará bien.

En pocas palabras: si creas un programa monolítico de ejecución única, probablemente tengas la mayoría de las potentes funciones de .NET con un compilador que crea código nativo. Pero las desventajas de tener un compilador JIT (instalación de marco, tiempos de arranque ligeramente más largos) realmente no superan los beneficios en la mayoría de los casos.