Pregunta

Estaba leyendo esta pregunta para descubrir las diferencias entre la máquina virtual Java y .NET CLR y la respuesta de Benji me hicieron preguntarme por qué las máquinas virtuales son necesarias en primer lugar.

Desde mi entendimiento de la explicación de Benji, el compilador JIT de una máquina virtual interpreta el código intermedio en el código de ensamblaje real que se ejecuta en la CPU. La razón por la que tiene que hacer esto es porque las CPU a menudo tienen diferentes números de registros y, según Benji, & "; Algunos registros tienen un propósito especial, y cada instrucción espera sus operandos en diferentes registros. &"; Esto tiene sentido, entonces, que es necesario un intérprete intermediario como la Máquina Virtual para que el mismo código se pueda ejecutar en cualquier CPU.

Pero, si ese es el caso, entonces lo que no entiendo es por qué el código C o C ++ compilado en el código de la máquina puede ejecutarse en cualquier computadora siempre que sea el sistema operativo correcto. ¿Por qué entonces un programa C que compilé en mi máquina Windows usando un Pentium podría ejecutarse en mi otra máquina Windows usando un AMD?

Si el código C puede ejecutarse en cualquier CPU, ¿cuál es el propósito de la máquina virtual? ¿Es para que se pueda ejecutar el mismo código en cualquier sistema operativo? Sé que Java tiene versiones de VM en casi cualquier sistema operativo, pero ¿hay un CLR para otros sistemas operativos además de Windows?

¿O hay algo más que me estoy perdiendo? ¿El sistema operativo hace alguna otra interpretación del código de ensamblaje que ejecuta para adaptarlo a la CPU en particular o algo así?

Tengo mucha curiosidad acerca de cómo funciona todo esto, por lo que agradecería una explicación clara.

Nota: La razón por la que no publiqué mis consultas como comentarios en la pregunta JVM vs. CLR es porque todavía no tengo suficientes puntos para publicar comentarios = b.

Editar: ¡Gracias por todas las excelentes respuestas! Parece que lo que me faltaba era que, aunque todos los procesadores tienen diferencias, hay una estandarización común, principalmente la arquitectura X86, que proporciona un conjunto lo suficientemente grande de características comunes para que el código C compilado en un procesador X86 funcione en su mayor parte en otro procesador X86. Esto fomenta la justificación de las máquinas virtuales, sin mencionar que olvidé la importancia de la recolección de basura.

¿Fue útil?

Solución

Los procesadores AMD e Intel usan el mismo conjunto de instrucciones y arquitectura de máquina (desde el punto de vista de la ejecución del código de máquina).

Los compiladores C y C ++ compilan en código máquina, con encabezados apropiados para el sistema operativo al que están dirigidos. Una vez compilados, dejan de asociarse de cualquier forma, forma o forma con el lenguaje en el que fueron compilados y son simplemente ejecutables binarios. (hay artefactos que pueden mostrar de qué idioma se compiló, pero ese no es el punto aquí)

Entonces, una vez compilados, se asocian a la máquina (X86, el conjunto de instrucciones y arquitectura Intel y AMD) y el sistema operativo.

Es por eso que pueden ejecutarse en cualquier máquina x86 compatible, y en cualquier sistema operativo compatible (win95 a través de winvista, para algún software).

Sin embargo, no pueden ejecutarse en una máquina OSX, incluso si se está ejecutando en un procesador Intel: el binario no es compatible a menos que ejecute software de emulación adicional (como paralelos o una máquina virtual con Windows).

Más allá de eso, si desea ejecutarlos en un procesador ARM, o MIPS, o PowerPC, entonces debe ejecutar un emulador de conjunto de instrucciones de máquina completo que interpreta el código de máquina binario de X86 en cualquier máquina que esté ejecutando. en.

Compare eso con .NET.

La máquina virtual .NET se fabrica como si hubiera procesadores mucho mejores en el mundo: procesadores que entiendan los objetos, la asignación de memoria y la recolección de basura, y otras construcciones de alto nivel. Es una máquina muy compleja y no se puede construir directamente en silicio ahora (con buen rendimiento), pero se puede escribir un emulador que le permita ejecutarse en cualquier procesador existente.

De repente, puede escribir un emulador específico de la máquina para cualquier procesador en el que desee ejecutar .NET, y luego CUALQUIER programa .NET puede ejecutarse en él. No es necesario preocuparse por el sistema operativo o la arquitectura de CPU subyacente: si hay una VM .NET, el software se ejecutará.

Pero vayamos un poco más lejos: una vez que tenga este lenguaje común, ¿por qué no hacer compiladores que conviertan cualquier otro lenguaje escrito en él?

Entonces ahora puede tener un compilador de lenguaje C, C #, C ++, Java, javascript, Basic, python, lua o cualquier otro que convierta el código escrito para que se ejecute en esta máquina virtual.

Has desasociado la máquina del lenguaje en 2 grados, y sin demasiado trabajo permites que cualquiera escriba cualquier código y lo ejecute en cualquier máquina, siempre que exista un compilador y una VM para mapear los dos grados de separación.

Si todavía se pregunta por qué esto es algo bueno, considere las primeras máquinas DOS y cuál fue la contribución real de Microsoft al mundo:

Autocad tenía que escribir controladores para cada impresora en la que podían imprimir. También lo hizo el loto 1-2-3. De hecho, si quería que su software imprimiera, tenía que escribir sus propios controladores. Si había 10 impresoras y 10 programas, entonces 100 piezas diferentes de esencialmente el mismo código tenían que escribirse por separado e independientemente.

Lo que Windows 3.1 intentó lograr (junto con GEM y tantas otras capas de abstracción) es hacer que el fabricante de la impresora escribiera un controlador para su impresora, y el programador escribió un controlador para la clase de impresora de Windows.

Ahora, con 10 programas y 10 impresoras, solo se deben escribir 20 piezas de código, y dado que el lado microsoft del código era el mismo para todos, entonces los ejemplos de MS significaron que tenía muy poco trabajo por hacer.

Ahora un programa no se limitaba solo a las 10 impresoras que eligieron admitir, sino a todas las impresoras cuyos fabricantes proporcionaron controladores en Windows.

El mismo problema está ocurriendo en el desarrollo de aplicaciones. Hay aplicaciones realmente buenas que no puedo usar porque no uso un MAC. Hay un montón de duplicaciones (¿cuántos procesadores de texto de clase mundial realmente necesitamos?).

Java estaba destinado a solucionar esto, pero tenía muchas limitaciones, algunas de las cuales no son realeslly resuelto.

.NET está más cerca, pero nadie está desarrollando máquinas virtuales de clase mundial para plataformas que no sean Windows (mono está tan cerca ... y aún no está allí).

Entonces ... Por eso necesitamos máquinas virtuales. Porque no quiero limitarme a un público más pequeño simplemente porque eligieron una combinación de sistema operativo / máquina diferente a la mía.

-Adam

Otros consejos

Su suposición de que el código C puede ejecutarse en cualquier procesador es incorrecta. Hay cosas como registros y endianness que harán que los programas C compilados no funcionen en absoluto en una plataforma, mientras que podrían funcionar en otra.

Sin embargo, hay ciertas similitudes que comparten los procesadores, por ejemplo, los procesadores Intel x86 y los procesadores AMD comparten un conjunto suficientemente grande de propiedades que la mayoría del código compilado contra uno se ejecutará en el otro. Sin embargo, si desea utilizar propiedades específicas del procesador, necesita un compilador o un conjunto de bibliotecas que lo hagan por usted.

En cuanto a por qué querría una máquina virtual, más allá de la afirmación de que manejará las diferencias en los procesadores por usted, también existe el hecho de que las máquinas virtuales ofrecen servicios de código que no están disponibles para programas compilados en C ++ (no administrado ) hoy.

El servicio más destacado ofrecido es la recolección de basura, ofrecida por CLR y JVM. Ambas máquinas virtuales le ofrecen este servicio de forma gratuita. Administran la memoria por ti.

También se ofrecen cosas como la verificación de límites, infracciones de acceso (aunque todavía es posible, son extremadamente difíciles).

El CLR también ofrece una forma de seguridad de código para usted.

Ninguno de estos se ofrece como parte del entorno de tiempo de ejecución básico para otros idiomas que no funcionan con una máquina virtual.

Puede obtener algunos de ellos utilizando bibliotecas, pero eso lo obliga a seguir un patrón de uso con la biblioteca, mientras que en .NET y Java los servicios que se le ofrecen a través de CLR y JVM son consistentes en su acceso.

Básicamente, permite el 'código administrado', lo que significa exactamente lo que dice: la máquina virtual administra el código mientras se ejecuta. Tres beneficios principales de esto son la compilación justo a tiempo, punteros administrados / recolección de basura y control de seguridad.

Para la compilación justo a tiempo, la máquina virtual observa la ejecución del código y, a medida que el código se ejecuta con más frecuencia, se optimiza para que se ejecute más rápido. No puedes hacer esto con código nativo.

Los punteros administrados también son más fáciles de optimizar porque la máquina virtual los rastrea a medida que avanzan, administrándolos de diferentes maneras según su tamaño y duración. Es difícil hacer esto en C ++ porque realmente no se puede saber a dónde irá un puntero solo leyendo el código.

La seguridad se explica por sí sola, la máquina virtual impide que el código haga cosas que no debería porque está mirando. Personalmente, creo que esa es probablemente la razón más importante por la que Microsoft eligió el código administrado para C #.

Básicamente, mi punto es que, dado que la máquina virtual puede ver el código a medida que sucede, puede hacer cosas que facilitan la vida del programador y hacen que el código sea más rápido.

La mayoría de los compiladores, incluso los compiladores de código nativo, usan algún tipo de lenguaje intermedio.

Esto se hace principalmente para reducir los costos de construcción del compilador. Hay muchos (N) lenguajes de programación en el mundo. También hay muchas plataformas de hardware (M) en el mundo. Si los compiladores trabajaron sin usar un lenguaje intermedio, el número total de & Quot; compiladores & Quot; eso debería escribirse para admitir todos los idiomas en todas las plataformas de hardware sería N * M.

Sin embargo, al definir un lenguaje intermedio y dividir un compilador en 2 partes, un front-end y un back-end, con el front-end compilando el código fuente en IL y el back-end compilando IL en código máquina, puede escapar con escribir solo compiladores N + M. Esto termina siendo un gran ahorro de costos.

La gran diferencia entre los compiladores CLR / JVM y los compiladores de código nativo es la forma en que los compiladores front-end y back-end están vinculados entre sí. En un compilador de código nativo, los dos componentes generalmente se combinan en el mismo ejecutable, y ambos se ejecutan cuando el programador presiona & Quot; build & Quot; en el IDE.

Con los compiladores CLR / JVM, el front-end y el back-end se ejecutan en diferentes momentos. El front end se ejecuta en tiempo de compilación, produciendo IL que realmente se envía a los clientes. El back-end se incorpora en un componente separado que se invoca en tiempo de ejecución.

Entonces, esto trae a colación la pregunta alternativa, " ¿Cuáles son los beneficios de retrasar la compilación de back-end hasta el tiempo de ejecución " ;?

La respuesta es: " Depende " ;.

Al retrasar la compilación de back-end hasta el tiempo de ejecución, es posible enviar un conjunto de binarios que pueden ejecutarse en múltiples plataformas de hardware. También hace posible que los programas aprovechen las mejoras en la tecnología de compilación de back-end sin ser redistribuidos. También puede proporcionar una base para implementar eficientemente muchas características de lenguaje dinámico. Finalmente, ofrece la capacidad de introducir restricciones de seguridad y confiabilidad entre bibliotecas (dlls) compiladas dinámicamente y compiladas por separado que no es posible con la compilación inicial de código de máquina.

Sin embargo, también hay inconvenientes. El análisis necesario para implementar amplias optimizaciones del compilador puede ser costoso. Esto significa que & Quot; JIT & Quot; los backends a menudo harán menos optimizaciones que los backends por adelantado. Esto puede dañar el rendimiento. Además, la necesidad de invocar el compilador en tiempo de ejecución también aumenta el tiempo necesario para cargar programas. Programas generados con & Quot; por adelantado & Quot; los compiladores no tienen esos problemas.

En primer lugar, el código de máquina no es la forma más baja de instrucciones para una CPU. Los CPUS x86 actuales interpretan el conjunto de instrucciones X86 en otro formato interno utilizando microcódigo. Las únicas personas que realmente programan microcódigo son los tipos de ingenieros de desarrollo de chips, que emulan fielmente y sin dolor el chip de instrucción x86 heredado para lograr el máximo rendimiento utilizando las tecnologías actuales.

Los tipos de desarrolladores siempre han estado agregando capas adicionales de abstracciones debido a la potencia y las características que aportan. Después de todo, las mejores abstracciones permiten que las nuevas aplicaciones se escriban de manera más rápida y confiable. A las empresas no les importa qué aspecto tienen o cómo codifican, solo quieren que el trabajo se realice de manera confiable y rápida. ¿Realmente importa si la versión C de una aplicación tarda unos milisegundos menos pero termina tardando el doble en desarrollarse?

La pregunta de velocidad casi no es un argumento, ya que muchas aplicaciones empresariales que sirven a millones de personas están escritas en plataformas / idiomas como Java, por ejemplo, GMail, GMaps. Olvídate de qué idioma / plataforma es más rápido. Lo que es más importante es que use los algoritmos correctos y escriba un código eficiente y haga el trabajo.

Los procesadores AMD e Intel tienen arquitectura x86, si desea ejecutar el programa c / c ++ en una arquitectura diferente, debe usar un compilador para esa arquitectura, el mismo ejecutable binario no se ejecutará en diferentes arquitecturas de procesador.

  

Sé que Java tiene versiones de VM en casi cualquier sistema operativo, pero ¿hay un CLR para otros sistemas operativos además de Windows?

Mono

De una manera muy simplificada, eso se debe a que Intel y AMD implementan el mismo lenguaje ensamblador, con el mismo número de registros, etc., etc.

Entonces su compilador de C compila código para trabajar en Linux. Ese ensamblaje está utilizando un Linux ABI , siempre que el programa de compilación se ejecute en Linux , en el ensamblado x86 y la firma de función correcta, entonces todo es excelente.

Ahora intente tomar ese código compilado y péguelo, diga Linux / PPC (por ejemplo, Linux en un iBook antiguo). Eso no va a funcionar. Donde lo haría un programa Java porque la JVM se ha implementado en la plataforma Linux / PPC.

El lenguaje de ensamblaje hoy en día es básicamente otra interfaz en la que un programador puede programar. x86 (32 bits) le permite acceder a eax, ebx, ecx, edx para registros enteros de propósito general y f00-f07 para coma flotante. Detrás de escena, la CPU en realidad tiene cientos de registros más, y mezcló esas cosas para exprimir el rendimiento.

Tiene razón en su análisis, java o C # podrían haber sido diseñados para compilar directamente para ejecutarse en cualquier máquina, y probablemente serían más rápidos si lo hicieran. Pero el enfoque de máquina virtual brinda un control completo del entorno en el que se ejecuta su código, la VM crea un entorno seguro que solo permite que los comandos con el acceso de seguridad adecuado realicen código potencialmente dañino, como cambiar la contraseña o actualizar un sector de arranque HD. Hay muchos otros beneficios, pero esa es la razón asesina. No puede obtener un StackOverflow en C # ...

Creo que la premisa de su pregunta es válida: ciertamente no es el primero en hacer esta pregunta. Por lo tanto, consulte http://llvm.org para ver un enfoque alternativo (que ahora es un proyecto en ejecución o patrocinado por Apple)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top