(Esta es una pregunta extremadamente novata).
He estado estudiando un poco sobre máquinas virtuales.
Resulta que muchos de ellos están diseñados de manera muy similar a las computadoras físicas o teóricas.
Leí que la JVM, por ejemplo, es una 'máquina de pila'. Lo que eso significa (y corrígeme si me equivoco) es que almacena toda su 'memoria temporal' en una pila, y realiza operaciones en esta pila para todos sus códigos de operación.
Por ejemplo, el código fuente 2 + 3
se traducirá a bytecode similar a:
push 2
push 3
add
Mi pregunta es esta:
Las JVM probablemente se escriben usando C / C ++ y tal. Si es así, ¿por qué la JVM no ejecuta el siguiente código C: 2 + 3
..? Quiero decir, ¿por qué necesita una pila, o en otros 'registros' de máquinas virtuales, como en una computadora física?
La CPU física subyacente se encarga de todo esto. ¿Por qué los escritores de VM simplemente no ejecutan el bytecode interpretado con instrucciones 'habituales' en el idioma con el que está programada la VM?
¿Por qué las máquinas virtuales necesitan emular hardware, cuando el hardware real ya lo hace por nosotros?
De nuevo, preguntas muy novatas. Gracias por tu ayuda
fuente
printf("hi");
: ¿se considera esto una VM? No tiene 'pila' ni 'registros' ni nada.Respuestas:
Una máquina, virtual o no, necesita un modelo de computación que describa cómo se realiza la computación en ella. Por definición, tan pronto como computa, implementa algún modelo de computación. La pregunta entonces es: ¿Qué modelo deberíamos elegir para nuestra VM? Las máquinas físicas están limitadas por lo que se puede hacer de manera efectiva y eficiente en el hardware. Pero, como observa, las máquinas virtuales no tienen tales restricciones, están definidas en software que utiliza lenguajes arbitrariamente de alto nivel.
De hecho, hay máquinas virtuales que son de alto nivel como usted describe. Se llaman lenguajes de programación . El estándar C, por ejemplo, dedica la mayor parte de sus páginas a definir un modelo para la llamada "máquina abstracta C", que describe cómo se comportan los programas C y, por extensión (como si fuera la regla), cómo un compilador (o intérprete) de C conforme debería comportarse.
Por supuesto, generalmente no lo llamamos máquina virtual. Una VM generalmente significa algo de nivel inferior, más cercano al hardware, que no está destinado a ser programado directamente, diseñado para ejecutarse de manera eficiente. Este sesgo de selección significa que algo que acepta código componible de alto nivel (como lo que usted describe) no se consideraría una VM porque se ejecuta código de alto nivel.
Pero para ir al grano, aquí hay algunas razones para hacer que una VM (como en, algo dirigido por un compilador de bytecode) esté basada en registros o similares. Las máquinas de apilar y registrar son extremadamente simples. Hay una secuencia de instrucciones, algunos estados y semántica para cada instrucción (una función Estado -> Estado). Sin reducciones complejas de árboles, sin precedencia de operadores. Analizarlo, analizarlo y ejecutarlo es muy simple, porque es un lenguaje mínimo (el azúcar sintáctico se compila) y está diseñado para ser leído en máquina en lugar de ser leído por humanos.
Por el contrario, analizar incluso los lenguajes tipo C más simples es bastante difícil, y su ejecución requiere análisis no locales como verificar y propagar tipos, resolver sobrecargas, mantener una tabla de símbolos, resolver identificadores de cadena , convertir texto lineal en un AST basado en precedencia , y así. Se basa en conceptos que son naturales para los humanos pero que deben ser cuidadosamente diseñados por máquinas.
El bytecode de JVM, por ejemplo, es emitido por
javac
. Prácticamente nunca necesita ser leído o escrito por humanos, por lo que es natural orientarlo hacia el consumo de las máquinas. Si lo optimiza para humanos, la JVM solo en cada inicio leería el código, lo analizaría, analizaría y luego lo convertiría en una representación intermedia que se pareciera a un modelo de máquina simplificado de todos modos . También podría eliminar al intermediario.fuente
System.out.println("hi");
es decir, se compila en alguna instrucción en una pila,int a = 7
se compila en una instrucción en la pila, etc.) hace que la ejecución del programa sea simple y más eficiente?2 + 3
se compilapush 2 push 3 add
. Eladd
paso al final es ejecutado por la JVM de todos modos mediante la ejecución del código C2 + 3
. No hay otra forma para que los programadores de la JVM hagan esto. ¿Por qué no compilarlo2 + 3
y hacer que la JVM solo ejecute el código C2 + 3
(suponiendo que esté escrito en C) de inmediato?2 + 3
el código fuente de JVM porque JVM tiene que trabajar con cualquier programa que realice cualquier operación en cualquier orden. Construir código fuente C y diferir a una implementación C simplemente empuja el mismo problema a la implementación C (y no se puede hacer fácilmente, y mucho menos eficientemente). Tiene que haber alguna estructura de datos que describa el programa, para que pueda ser interpretado y compilado JIT, y el "código fuente legible por humanos" es una elección horrible de estructura de datos por las razones descritas anteriormente.a + b
? Entonces los valores a agregar no provieneni.argument{1,2}
, se cargan desde variables locales. ¿Qué hay defrobnicate(x[i]) + (Foo.bar() * 2)
? Con este diseño, solo hay unaadd
operación (paraint
) y funciona independientemente de cómo se calcularon los sumandos. Además, una instrucción que agrega solo literales enteros no tendría sentido: su resultado también podría calcularse previamente (es decir, en lugar deadd(2,3)
serlopush(5)
).Esta respuesta se centra en la JVM, pero de hecho se aplica a cualquier VM.
No lo hacen, pero hace que la VM sea mucho más simple y portátil: una VM que emula hardware puede usar el mismo modelo computacional que cualquier CPU de hardware.
La JVM en particular se construyó con la portabilidad en mente, de hecho, se construyó para que incluso se pudiera implementar en hardware (puede ser difícil de creer hoy, pero el origen de Java se encontraba en el mundo integrado, específicamente, los controladores para televisión interactiva )
Si tiene un objetivo como este, es deseable que la VM funcione lo más cerca posible de una máquina física, ya que la traducción al código real de la máquina se vuelve más fácil y, por lo tanto, más rápida. Una vez que tenga los códigos de operación de la VM, en teoría, todo lo que tiene que hacer es traducir a los códigos de operación de la CPU en la que realmente se ejecuta el programa. En la práctica no es exactamente así de simple.
El uso de un modelo de máquina virtual basado en pila tiene la ventaja de que puede transferirse fácilmente tanto a máquinas de registro como a máquinas de pila, mientras que lo contrario no es necesariamente cierto. Una máquina virtual basada en registros necesitaría hacer suposiciones sobre el número de registros, el tamaño de los registros, etc. Con una máquina de pila, no se necesitan tales suposiciones.
Bueno, eso es lo que hacen esas máquinas virtuales: interpretan el código de bytes. Incluso la JVM hace eso, al menos antes de que JIT (justo a tiempo) entre en funcionamiento: interpreta los códigos de bytes y ejecuta las declaraciones en el lenguaje en el que se escribió la JVM (generalmente C o C ++, pero incluso hay una escrita en JavaScript, Doppio ). Sin embargo, tenga en cuenta que incluso esas declaraciones fueron traducidas al código de máquina por un compilador y en realidad se parecen mucho a lo que produce el compilador de Java, es decir, usan registros y la pila para realizar su trabajo. Tenga en cuenta que el uso de lenguajes "interpretados" frente a "compilados" se vuelve algo borroso en este punto.
fuente
¿Por qué las máquinas virtuales deben ser "máquinas de pila" o "máquinas de registro", etc.?
Ellos no. Si necesita una máquina virtual, puede ser cualquier cosa.
Las máquinas virtuales existentes han aparecido como soluciones a situaciones como: Me ha surgido una idea realmente brillante, ¡he inventado un nuevo lenguaje de programación! Pero tengo que generar código. (¡Qué tarea más aburrida!) Pero no quiero generar el código i8086 porque es feo, y no quiero generar el código 68k porque todos los demás están usando Intel. También hay VAX, pero no tengo ningún VAX, ni una computadora ni un libro VAX. Por lo tanto, generaré código para algún procesador que no existe físicamente e implementaré ese procesador en el software. La especificación de esa VM hará un capítulo en mi tesis. En teoría, será posible compilarlo en el código nativo de cualquier procesador, pero ese no seré yo.
Por otro lado, la notación como "2 + 3" probablemente no será utilizada por las máquinas virtuales en un futuro previsible porque implica hacer muchas transformaciones antes de que algo pueda ejecutarse.
fuente
Para responder a la pregunta real que se hizo. El término "MÁQUINA virtual" significa que TODO el software / hardware se simula / emula. Si usa el software / hardware subyacente para ejecutar las instrucciones, entonces no tiene una VM, tiene un compilador / intérprete.
fuente